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