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