001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.doxia.sink.impl; 020 021import javax.swing.text.AttributeSet; 022 023import java.util.Collections; 024import java.util.Enumeration; 025import java.util.LinkedHashMap; 026import java.util.Map; 027import java.util.Map.Entry; 028import java.util.Set; 029 030import org.apache.maven.doxia.sink.SinkEventAttributes; 031 032/** 033 * Implementation of MutableAttributeSet using a LinkedHashMap. 034 * 035 * @author ltheussl 036 * @since 1.1 037 */ 038public class SinkEventAttributeSet implements SinkEventAttributes, Cloneable { 039 /** 040 * An unmodifiable attribute set containing only an underline attribute. 041 */ 042 public static final SinkEventAttributes UNDERLINE; 043 044 /** 045 * An unmodifiable attribute set containing only an overline attribute. 046 */ 047 public static final SinkEventAttributes OVERLINE; 048 049 /** 050 * An unmodifiable attribute set containing only a linethrough attribute. 051 */ 052 public static final SinkEventAttributes LINETHROUGH; 053 054 /** 055 * An unmodifiable attribute set containing only a source attribute. 056 */ 057 public static final SinkEventAttributes SOURCE; 058 059 /** 060 * An unmodifiable attribute set containing only a bold attribute. 061 */ 062 public static final SinkEventAttributes BOLD; 063 064 /** 065 * An unmodifiable attribute set containing only an italic attribute. 066 */ 067 public static final SinkEventAttributes ITALIC; 068 069 /** 070 * An unmodifiable attribute set containing only a monospaced attribute. 071 */ 072 public static final SinkEventAttributes MONOSPACED; 073 074 /** 075 * An unmodifiable attribute set containing only a left attribute. 076 */ 077 public static final SinkEventAttributes LEFT; 078 079 /** 080 * An unmodifiable attribute set containing only a right attribute. 081 */ 082 public static final SinkEventAttributes RIGHT; 083 084 /** 085 * An unmodifiable attribute set containing only a center attribute. 086 */ 087 public static final SinkEventAttributes CENTER; 088 089 /** 090 * An unmodifiable attribute set containing only a justify attribute. 091 */ 092 public static final SinkEventAttributes JUSTIFY; 093 094 static { 095 UNDERLINE = new SinkEventAttributeSet(DECORATION, "underline").unmodifiable(); 096 OVERLINE = new SinkEventAttributeSet(DECORATION, "overline").unmodifiable(); 097 LINETHROUGH = new SinkEventAttributeSet(DECORATION, "line-through").unmodifiable(); 098 SOURCE = new SinkEventAttributeSet(DECORATION, "source").unmodifiable(); 099 100 BOLD = new SinkEventAttributeSet(STYLE, "bold").unmodifiable(); 101 ITALIC = new SinkEventAttributeSet(STYLE, "italic").unmodifiable(); 102 MONOSPACED = new SinkEventAttributeSet(STYLE, "monospaced").unmodifiable(); 103 104 LEFT = new SinkEventAttributeSet(ALIGN, "left").unmodifiable(); 105 RIGHT = new SinkEventAttributeSet(ALIGN, "right").unmodifiable(); 106 CENTER = new SinkEventAttributeSet(ALIGN, "center").unmodifiable(); 107 JUSTIFY = new SinkEventAttributeSet(ALIGN, "justify").unmodifiable(); 108 } 109 110 private Map<String, Object> attribs; 111 112 private AttributeSet resolveParent; 113 114 /** 115 * Constructs a new, empty SinkEventAttributeSet with default size 5. 116 */ 117 public SinkEventAttributeSet() { 118 this(5); 119 } 120 121 /** 122 * Constructs a new, empty SinkEventAttributeSet with the specified initial size. 123 * 124 * @param size the initial number of attribs. 125 */ 126 public SinkEventAttributeSet(int size) { 127 attribs = new LinkedHashMap<>(size); 128 } 129 130 /** 131 * Constructs a new SinkEventAttributeSet with the attribute name-value 132 * mappings as given by the specified String array. 133 * Each even index of the array is an attribute name, and the following odd index is the corresponding attribute value. 134 * This constructor only supports String attribute values. 135 * 136 * @param attributes the specified String array. If the length of this array 137 * is not an even number, an IllegalArgumentException is thrown. 138 */ 139 public SinkEventAttributeSet(String... attributes) { 140 int n = attributes.length; 141 142 if ((n % 2) != 0) { 143 throw new IllegalArgumentException("Missing attribute!"); 144 } 145 146 attribs = new LinkedHashMap<>(n / 2); 147 148 for (int i = 0; i < n; i += 2) { 149 attribs.put(attributes[i], attributes[i + 1]); 150 } 151 } 152 153 /** 154 * Constructs a new SinkEventAttributeSet with the same attribute name-value 155 * mappings as in the specified AttributeSet. 156 * 157 * @param attributes the specified AttributeSet. 158 */ 159 public SinkEventAttributeSet(AttributeSet attributes) { 160 attribs = new LinkedHashMap<>(attributes.getAttributeCount()); 161 162 Enumeration<?> names = attributes.getAttributeNames(); 163 164 while (names.hasMoreElements()) { 165 Object name = names.nextElement(); 166 167 attribs.put(name.toString(), attributes.getAttribute(name)); 168 } 169 } 170 171 /** 172 * Replace this AttributeSet by an unmodifiable view of itself. 173 * Any subsequent attempt to add, remove or modify the underlying mapping 174 * will result in an UnsupportedOperationException. 175 * 176 * @return an unmodifiable view of this AttributeSet. 177 * @since 1.1.1 178 */ 179 public SinkEventAttributeSet unmodifiable() { 180 this.attribs = Collections.unmodifiableMap(attribs); 181 182 return this; 183 } 184 185 /** 186 * Checks whether the set of attribs is empty. 187 * 188 * @return true if the set is empty. 189 */ 190 public boolean isEmpty() { 191 return attribs.isEmpty(); 192 } 193 194 /** 195 * {@inheritDoc} 196 * 197 * @return a int. 198 */ 199 public int getAttributeCount() { 200 return attribs.size(); 201 } 202 203 public boolean isDefined(Object attrName) { 204 return attribs.containsKey(attrName); 205 } 206 207 public boolean isEqual(AttributeSet attr) { 208 return ((getAttributeCount() == attr.getAttributeCount()) && containsAttributes(attr)); 209 } 210 211 /** 212 * {@inheritDoc} 213 * 214 * @return a {@link javax.swing.text.AttributeSet} object. 215 */ 216 public AttributeSet copyAttributes() { 217 return ((AttributeSet) clone()); 218 } 219 220 /** 221 * {@inheritDoc} 222 * 223 * @return a {@link java.util.Enumeration} object. 224 */ 225 public Enumeration<String> getAttributeNames() { 226 return Collections.enumeration(attribs.keySet()); 227 } 228 229 public Object getAttribute(Object key) { 230 Object value = attribs.get(key); 231 232 if (value == null) { 233 AttributeSet parent = getResolveParent(); 234 235 if (parent != null) { 236 value = parent.getAttribute(key); 237 } 238 } 239 240 return value; 241 } 242 243 public boolean containsAttribute(Object name, Object value) { 244 return value.equals(getAttribute(name)); 245 } 246 247 public boolean containsAttributes(AttributeSet attributes) { 248 boolean result = true; 249 250 Enumeration<?> names = attributes.getAttributeNames(); 251 252 while (result && names.hasMoreElements()) { 253 Object name = names.nextElement(); 254 result = attributes.getAttribute(name).equals(getAttribute(name)); 255 } 256 257 return result; 258 } 259 260 /** 261 * {@inheritDoc} 262 * 263 * Adds an attribute with the given name and value. 264 */ 265 public void addAttribute(Object name, Object value) { 266 attribs.put(name.toString(), value); 267 } 268 269 public void addAttributes(AttributeSet attributes) { 270 if (attributes == null || attributes.getAttributeCount() == 0) { 271 return; 272 } 273 274 Enumeration<?> names = attributes.getAttributeNames(); 275 276 while (names.hasMoreElements()) { 277 Object name = names.nextElement(); 278 279 addAttribute(name, attributes.getAttribute(name)); 280 } 281 } 282 283 public void removeAttribute(Object name) { 284 attribs.remove(name); 285 } 286 287 public void removeAttributes(Enumeration<?> names) { 288 while (names.hasMoreElements()) { 289 removeAttribute(names.nextElement()); 290 } 291 } 292 293 /** 294 * {@inheritDoc} 295 * 296 * @param attributes a {@link javax.swing.text.AttributeSet} object. 297 */ 298 public void removeAttributes(AttributeSet attributes) { 299 if (attributes == null) { 300 return; 301 } else if (attributes == this) { 302 attribs.clear(); 303 } else { 304 Enumeration<?> names = attributes.getAttributeNames(); 305 306 while (names.hasMoreElements()) { 307 Object name = names.nextElement(); 308 Object value = attributes.getAttribute(name); 309 310 if (value.equals(getAttribute(name))) { 311 removeAttribute(name); 312 } 313 } 314 } 315 } 316 317 /** 318 * {@inheritDoc} 319 * 320 * @return a {@link javax.swing.text.AttributeSet} object. 321 */ 322 public AttributeSet getResolveParent() { 323 return this.resolveParent; 324 } 325 326 public void setResolveParent(AttributeSet parent) { 327 this.resolveParent = parent; 328 } 329 330 @Override 331 public Set<Entry<String, Object>> entrySet() { 332 return attribs.entrySet(); 333 } 334 335 @Override 336 public Object clone() { 337 SinkEventAttributeSet attr = new SinkEventAttributeSet(attribs.size()); 338 attr.attribs = new LinkedHashMap<>(attribs); 339 340 if (resolveParent != null) { 341 attr.resolveParent = resolveParent.copyAttributes(); 342 } 343 344 return attr; 345 } 346 347 @Override 348 public int hashCode() { 349 final int parentHash = (resolveParent == null ? 0 : resolveParent.hashCode()); 350 351 return attribs.hashCode() + parentHash; 352 } 353 354 @Override 355 public boolean equals(Object obj) { 356 if (this == obj) { 357 return true; 358 } 359 360 if (obj instanceof SinkEventAttributeSet) { 361 return isEqual((SinkEventAttributeSet) obj); 362 } 363 364 return false; 365 } 366 367 @Override 368 public String toString() { 369 StringBuilder s = new StringBuilder(); 370 Enumeration<String> names = getAttributeNames(); 371 372 while (names.hasMoreElements()) { 373 String key = names.nextElement(); 374 String value = getAttribute(key).toString(); 375 376 s.append(' ').append(key).append('=').append(value); 377 } 378 379 return s.toString(); 380 } 381 382 /** 383 * Attribute sets for the semantic attribute. 384 */ 385 public static class Semantics { 386 /** 387 * An unmodifiable attribute set containing only an emphasis attribute. 388 */ 389 public static final SinkEventAttributes EMPHASIS; 390 391 /** 392 * An unmodifiable attribute set containing only a strong attribute. 393 */ 394 public static final SinkEventAttributes STRONG; 395 396 /** 397 * An unmodifiable attribute set containing only a small attribute. 398 */ 399 public static final SinkEventAttributes SMALL; 400 401 /** 402 * An unmodifiable attribute set containing only a line-through attribute. 403 */ 404 public static final SinkEventAttributes LINE_THROUGH; 405 406 /** 407 * An unmodifiable attribute set containing only a citation attribute. 408 */ 409 public static final SinkEventAttributes CITATION; 410 411 /** 412 * An unmodifiable attribute set containing only a quote attribute. 413 */ 414 public static final SinkEventAttributes QUOTE; 415 416 /** 417 * An unmodifiable attribute set containing only a definition attribute. 418 */ 419 public static final SinkEventAttributes DEFINITION; 420 421 /** 422 * An unmodifiable attribute set containing only an abbreviation attribute. 423 */ 424 public static final SinkEventAttributes ABBREVIATION; 425 426 /** 427 * An unmodifiable attribute set containing only an italic attribute. 428 */ 429 public static final SinkEventAttributes ITALIC; 430 431 /** 432 * An unmodifiable attribute set containing only a bold attribute. 433 */ 434 public static final SinkEventAttributes BOLD; 435 436 /** 437 * An unmodifiable attribute set containing only a monospaced attribute. 438 */ 439 public static final SinkEventAttributes MONOSPACED; 440 441 /** 442 * An unmodifiable attribute set containing only a code attribute. 443 */ 444 public static final SinkEventAttributes CODE; 445 446 /** 447 * An unmodifiable attribute set containing only a variable attribute. 448 */ 449 public static final SinkEventAttributes VARIABLE; 450 451 /** 452 * An unmodifiable attribute set containing only a sample attribute. 453 */ 454 public static final SinkEventAttributes SAMPLE; 455 456 /** 457 * An unmodifiable attribute set containing only a keyboard attribute. 458 */ 459 public static final SinkEventAttributes KEYBOARD; 460 461 /** 462 * An unmodifiable attribute set containing only a superscript attribute. 463 */ 464 public static final SinkEventAttributes SUPERSCRIPT; 465 466 /** 467 * An unmodifiable attribute set containing only a subscript attribute. 468 */ 469 public static final SinkEventAttributes SUBSCRIPT; 470 471 /** 472 * An unmodifiable attribute set containing only an annotation attribute. 473 */ 474 public static final SinkEventAttributes ANNOTATION; 475 476 /** 477 * An unmodifiable attribute set containing only a highlight attribute. 478 */ 479 public static final SinkEventAttributes HIGHLIGHT; 480 481 /** 482 * An unmodifiable attribute set containing only a ruby attribute. 483 */ 484 public static final SinkEventAttributes RUBY; 485 486 /** 487 * An unmodifiable attribute set containing only a rubyBase attribute. 488 */ 489 public static final SinkEventAttributes RUBY_BASE; 490 491 /** 492 * An unmodifiable attribute set containing only a rubyText attribute. 493 */ 494 public static final SinkEventAttributes RUBY_TEXT; 495 496 /** 497 * An unmodifiable attribute set containing only a rubyTextContainer attribute. 498 */ 499 public static final SinkEventAttributes RUBY_TEXT_CONTAINER; 500 501 /** 502 * An unmodifiable attribute set containing only a rubyParentheses attribute. 503 */ 504 public static final SinkEventAttributes RUBY_PARANTHESES; 505 506 /** 507 * An unmodifiable attribute set containing only a bidirectionalIsolation attribute. 508 */ 509 public static final SinkEventAttributes BIDIRECTIONAL_ISOLATION; 510 511 /** 512 * An unmodifiable attribute set containing only a bidirectionalOverride attribute. 513 */ 514 public static final SinkEventAttributes BIDIRECTIONAL_OVERRIDE; 515 516 /** 517 * An unmodifiable attribute set containing only a phrase attribute. 518 */ 519 public static final SinkEventAttributes PHRASE; 520 521 /** 522 * An unmodifiable attribute set containing only an insert attribute. 523 */ 524 public static final SinkEventAttributes INSERT; 525 526 /** 527 * An unmodifiable attribute set containing only a delete attribute. 528 */ 529 public static final SinkEventAttributes DELETE; 530 531 static { 532 EMPHASIS = new SinkEventAttributeSet(SEMANTICS, "emphasis").unmodifiable(); 533 STRONG = new SinkEventAttributeSet(SEMANTICS, "strong").unmodifiable(); 534 SMALL = new SinkEventAttributeSet(SEMANTICS, "small").unmodifiable(); 535 LINE_THROUGH = new SinkEventAttributeSet(SEMANTICS, "line-through").unmodifiable(); 536 CITATION = new SinkEventAttributeSet(SEMANTICS, "citation").unmodifiable(); 537 QUOTE = new SinkEventAttributeSet(SEMANTICS, "quote").unmodifiable(); 538 DEFINITION = new SinkEventAttributeSet(SEMANTICS, "definition").unmodifiable(); 539 ABBREVIATION = new SinkEventAttributeSet(SEMANTICS, "abbreviation").unmodifiable(); 540 ITALIC = new SinkEventAttributeSet(SEMANTICS, "italic").unmodifiable(); 541 BOLD = new SinkEventAttributeSet(SEMANTICS, "bold").unmodifiable(); 542 MONOSPACED = new SinkEventAttributeSet(SEMANTICS, "monospaced").unmodifiable(); 543 CODE = new SinkEventAttributeSet(SEMANTICS, "code").unmodifiable(); 544 VARIABLE = new SinkEventAttributeSet(SEMANTICS, "variable").unmodifiable(); 545 SAMPLE = new SinkEventAttributeSet(SEMANTICS, "sample").unmodifiable(); 546 KEYBOARD = new SinkEventAttributeSet(SEMANTICS, "keyboard").unmodifiable(); 547 SUPERSCRIPT = new SinkEventAttributeSet(SEMANTICS, "superscript").unmodifiable(); 548 SUBSCRIPT = new SinkEventAttributeSet(SEMANTICS, "subscript").unmodifiable(); 549 ANNOTATION = new SinkEventAttributeSet(SEMANTICS, "annotation").unmodifiable(); 550 HIGHLIGHT = new SinkEventAttributeSet(SEMANTICS, "highlight").unmodifiable(); 551 RUBY = new SinkEventAttributeSet(SEMANTICS, "ruby").unmodifiable(); 552 RUBY_BASE = new SinkEventAttributeSet(SEMANTICS, "rubyBase").unmodifiable(); 553 RUBY_TEXT = new SinkEventAttributeSet(SEMANTICS, "rubyText").unmodifiable(); 554 RUBY_TEXT_CONTAINER = new SinkEventAttributeSet(SEMANTICS, "rubyTextContainer").unmodifiable(); 555 RUBY_PARANTHESES = new SinkEventAttributeSet(SEMANTICS, "rubyParentheses").unmodifiable(); 556 BIDIRECTIONAL_ISOLATION = new SinkEventAttributeSet(SEMANTICS, "bidirectionalIsolation").unmodifiable(); 557 BIDIRECTIONAL_OVERRIDE = new SinkEventAttributeSet(SEMANTICS, "bidirectionalOverride").unmodifiable(); 558 PHRASE = new SinkEventAttributeSet(SEMANTICS, "phrase").unmodifiable(); 559 INSERT = new SinkEventAttributeSet(SEMANTICS, "insert").unmodifiable(); 560 DELETE = new SinkEventAttributeSet(SEMANTICS, "delete").unmodifiable(); 561 } 562 } 563}