View Javadoc
1   package org.apache.maven.doxia.sink.impl;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.Collections;
23  import java.util.Enumeration;
24  import java.util.LinkedHashMap;
25  import java.util.Map;
26  
27  import javax.swing.text.AttributeSet;
28  
29  import org.apache.maven.doxia.sink.SinkEventAttributes;
30  
31  /**
32   * Implementation of MutableAttributeSet using a LinkedHashMap.
33   *
34   * @author ltheussl
35   * @since 1.1
36   */
37  public class SinkEventAttributeSet
38      implements SinkEventAttributes, Cloneable
39  {
40      /**
41       * An unmodifiable attribute set containing only an underline attribute.
42       */
43      public static final SinkEventAttributes UNDERLINE;
44  
45      /**
46       * An unmodifiable attribute set containing only an overline attribute.
47       */
48      public static final SinkEventAttributes OVERLINE;
49  
50      /**
51       * An unmodifiable attribute set containing only a linethrough attribute.
52       */
53      public static final SinkEventAttributes LINETHROUGH;
54  
55      /**
56       * An unmodifiable attribute set containing only a boxed attribute.
57       */
58      public static final SinkEventAttributes BOXED;
59  
60      /**
61       * An unmodifiable attribute set containing only a bold attribute.
62       */
63      public static final SinkEventAttributes BOLD;
64  
65      /**
66       * An unmodifiable attribute set containing only an italic attribute.
67       */
68      public static final SinkEventAttributes ITALIC;
69  
70      /**
71       * An unmodifiable attribute set containing only a monospaced attribute.
72       */
73      public static final SinkEventAttributes MONOSPACED;
74  
75      /**
76       * An unmodifiable attribute set containing only a left attribute.
77       */
78      public static final SinkEventAttributes LEFT;
79  
80      /**
81       * An unmodifiable attribute set containing only a right attribute.
82       */
83      public static final SinkEventAttributes RIGHT;
84  
85      /**
86       * An unmodifiable attribute set containing only a center attribute.
87       */
88      public static final SinkEventAttributes CENTER;
89  
90      /**
91       * An unmodifiable attribute set containing only a justify attribute.
92       */
93      public static final SinkEventAttributes JUSTIFY;
94  
95  
96      static
97      {
98          UNDERLINE = new SinkEventAttributeSet( DECORATION, "underline" ).unmodifiable();
99          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 }