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   * @version $Id: SinkEventAttributeSet.html 979316 2016-02-02 21:51:43Z hboutemy $
36   * @since 1.1
37   */
38  public class SinkEventAttributeSet
39      implements SinkEventAttributes, Cloneable
40  {
41      /**
42       * An unmodifiable attribute set containing only an underline attribute.
43       */
44      public static final SinkEventAttributes UNDERLINE;
45  
46      /**
47       * An unmodifiable attribute set containing only an overline attribute.
48       */
49      public static final SinkEventAttributes OVERLINE;
50  
51      /**
52       * An unmodifiable attribute set containing only a linethrough attribute.
53       */
54      public static final SinkEventAttributes LINETHROUGH;
55  
56      /**
57       * An unmodifiable attribute set containing only a boxed attribute.
58       */
59      public static final SinkEventAttributes BOXED;
60  
61      /**
62       * An unmodifiable attribute set containing only a bold attribute.
63       */
64      public static final SinkEventAttributes BOLD;
65  
66      /**
67       * An unmodifiable attribute set containing only an italic attribute.
68       */
69      public static final SinkEventAttributes ITALIC;
70  
71      /**
72       * An unmodifiable attribute set containing only a monospaced attribute.
73       */
74      public static final SinkEventAttributes MONOSPACED;
75  
76      /**
77       * An unmodifiable attribute set containing only a left attribute.
78       */
79      public static final SinkEventAttributes LEFT;
80  
81      /**
82       * An unmodifiable attribute set containing only a right attribute.
83       */
84      public static final SinkEventAttributes RIGHT;
85  
86      /**
87       * An unmodifiable attribute set containing only a center attribute.
88       */
89      public static final SinkEventAttributes CENTER;
90  
91      /**
92       * An unmodifiable attribute set containing only a justify attribute.
93       */
94      public static final SinkEventAttributes JUSTIFY;
95  
96  
97      static
98      {
99          UNDERLINE = new SinkEventAttributeSet( new String[] {DECORATION, "underline"} ).unmodifiable();
100         OVERLINE = new SinkEventAttributeSet( new String[] {DECORATION, "overline"} ).unmodifiable();
101         LINETHROUGH = new SinkEventAttributeSet( new String[] {DECORATION, "line-through"} ).unmodifiable();
102         BOXED = new SinkEventAttributeSet( new String[] {DECORATION, "boxed"} ).unmodifiable();
103 
104         BOLD = new SinkEventAttributeSet( new String[] {STYLE, "bold"} ).unmodifiable();
105         ITALIC = new SinkEventAttributeSet( new String[] {STYLE, "italic"} ).unmodifiable();
106         MONOSPACED = new SinkEventAttributeSet( new String[] {STYLE, "monospaced"} ).unmodifiable();
107 
108         LEFT = new SinkEventAttributeSet( new String[] {ALIGN, "left"} ).unmodifiable();
109         RIGHT = new SinkEventAttributeSet( new String[] {ALIGN, "right"} ).unmodifiable();
110         CENTER = new SinkEventAttributeSet( new String[] {ALIGN, "center"} ).unmodifiable();
111         JUSTIFY = new SinkEventAttributeSet( new String[] {ALIGN, "justify"} ).unmodifiable();
112     }
113 
114     private Map<String, Object> attribs;
115 
116     private AttributeSet resolveParent;
117 
118     /**
119      * Constructs a new, empty SinkEventAttributeSet with default size 5.
120      */
121     public SinkEventAttributeSet()
122     {
123         this( 5 );
124     }
125 
126     /**
127      * Constructs a new, empty SinkEventAttributeSet with the specified initial size.
128      *
129      * @param size the initial number of attribs.
130      */
131     public SinkEventAttributeSet( int size )
132     {
133         attribs = new LinkedHashMap<String, Object>( size );
134     }
135 
136     /**
137      * Constructs a new SinkEventAttributeSet with the attribute name-value
138      * mappings as given by the specified String array.
139      *
140      * @param attributes the specified String array. If the length of this array
141      * is not an even number, an IllegalArgumentException is thrown.
142      */
143     public SinkEventAttributeSet( String... attributes )
144     {
145         int n = attributes.length;
146 
147         if ( ( n % 2 ) != 0 )
148         {
149             throw new IllegalArgumentException( "Missing attribute!" );
150         }
151 
152         attribs = new LinkedHashMap<String, Object>( n / 2 );
153 
154         for ( int i = 0; i < n; i += 2 )
155         {
156             attribs.put( attributes[i], attributes[i + 1] );
157         }
158     }
159 
160     /**
161      * Constructs a new SinkEventAttributeSet with the same attribute name-value
162      * mappings as in the specified AttributeSet.
163      *
164      * @param attributes the specified AttributeSet.
165      */
166     public SinkEventAttributeSet( AttributeSet attributes )
167     {
168         attribs = new LinkedHashMap<String, Object>( attributes.getAttributeCount() );
169 
170         Enumeration<?> names = attributes.getAttributeNames();
171 
172         while ( names.hasMoreElements() )
173         {
174             Object name = names.nextElement();
175 
176             attribs.put( name.toString(), attributes.getAttribute( name ) );
177         }
178     }
179 
180     /**
181      * Replace this AttributeSet by an unmodifiable view of itself.
182      * Any subsequent attempt to add, remove or modify the underlying mapping
183      * will result in an UnsupportedOperationException.
184      *
185      * @return an unmodifiable view of this AttributeSet.
186      *
187      * @since 1.1.1
188      */
189     public SinkEventAttributeSet unmodifiable()
190     {
191         this.attribs = Collections.unmodifiableMap( attribs );
192 
193         return this;
194     }
195 
196     /**
197      * Checks whether the set of attribs is empty.
198      *
199      * @return true if the set is empty.
200      */
201     public boolean isEmpty()
202     {
203         return attribs.isEmpty();
204     }
205 
206     /** {@inheritDoc} */
207     public int getAttributeCount()
208     {
209         return attribs.size();
210     }
211 
212     /** {@inheritDoc} */
213     public boolean isDefined( Object attrName )
214     {
215         return attribs.containsKey( attrName );
216     }
217 
218     /** {@inheritDoc} */
219     public boolean isEqual( AttributeSet attr )
220     {
221         return ( ( getAttributeCount() == attr.getAttributeCount() )
222                 && containsAttributes( attr ) );
223     }
224 
225     /** {@inheritDoc} */
226     public AttributeSet copyAttributes()
227     {
228         return ( (AttributeSet) clone() );
229     }
230 
231     /** {@inheritDoc} */
232     public Enumeration<String> getAttributeNames()
233     {
234         return Collections.enumeration( attribs.keySet() );
235     }
236 
237     /** {@inheritDoc} */
238     public Object getAttribute( Object key  )
239     {
240         Object value = attribs.get( key  );
241 
242         if ( value == null )
243         {
244             AttributeSet parent = getResolveParent();
245 
246             if ( parent != null )
247             {
248                 value = parent.getAttribute( key  );
249             }
250         }
251 
252         return value;
253     }
254 
255     /** {@inheritDoc} */
256     public boolean containsAttribute( Object name, Object value )
257     {
258         return value.equals( getAttribute( name ) );
259     }
260 
261     /** {@inheritDoc} */
262     public boolean containsAttributes( AttributeSet attributes )
263     {
264         boolean result = true;
265 
266         Enumeration<?> names = attributes.getAttributeNames();
267 
268         while ( result && names.hasMoreElements() )
269         {
270             Object name = names.nextElement();
271             result = attributes.getAttribute( name ).equals( getAttribute( name ) );
272         }
273 
274         return result;
275     }
276 
277     /**
278      * {@inheritDoc}
279      *
280      * Adds an attribute with the given name and value.
281      */
282     public void addAttribute( Object name, Object value )
283     {
284         attribs.put( name.toString(), value );
285     }
286 
287     /** {@inheritDoc} */
288     public void addAttributes( AttributeSet attributes  )
289     {
290         if ( attributes == null || attributes.getAttributeCount() == 0 )
291         {
292             return;
293         }
294 
295         Enumeration<?> names = attributes.getAttributeNames();
296 
297         while ( names.hasMoreElements() )
298         {
299             Object name = names.nextElement();
300 
301             addAttribute( name, attributes.getAttribute( name ) );
302         }
303     }
304 
305     /** {@inheritDoc} */
306     public void removeAttribute( Object name )
307     {
308         attribs.remove( name );
309     }
310 
311     /** {@inheritDoc} */
312     public void removeAttributes( Enumeration<?> names )
313     {
314         while ( names.hasMoreElements() )
315         {
316             removeAttribute( names.nextElement() );
317         }
318     }
319 
320     /** {@inheritDoc} */
321     public void removeAttributes( AttributeSet attributes  )
322     {
323         if ( attributes == null )
324         {
325             return;
326         }
327         else if ( attributes == this )
328         {
329             attribs.clear();
330         }
331         else
332         {
333             Enumeration<?> names = attributes.getAttributeNames();
334 
335             while ( names.hasMoreElements() )
336             {
337                 Object name = names.nextElement();
338                 Object value = attributes.getAttribute( name );
339 
340                 if ( value.equals( getAttribute( name ) ) )
341                 {
342                     removeAttribute( name );
343                 }
344             }
345         }
346     }
347 
348     /** {@inheritDoc} */
349     public AttributeSet getResolveParent()
350     {
351         return this.resolveParent;
352     }
353 
354     /** {@inheritDoc} */
355     public void setResolveParent( AttributeSet parent )
356     {
357         this.resolveParent = parent;
358     }
359 
360     /** {@inheritDoc} */
361     @Override
362     public Object clone()
363     {
364         SinkEventAttributeSet attr = new SinkEventAttributeSet( attribs.size() );
365         attr.attribs = new LinkedHashMap<String, Object>( attribs );
366 
367         if ( resolveParent != null )
368         {
369             attr.resolveParent = resolveParent.copyAttributes();
370         }
371 
372         return attr;
373     }
374 
375     /** {@inheritDoc} */
376     @Override
377     public int hashCode()
378     {
379         final int parentHash = ( resolveParent == null ? 0 : resolveParent.hashCode() );
380 
381         return attribs.hashCode() + parentHash;
382     }
383 
384     /** {@inheritDoc} */
385     @Override
386     public boolean equals( Object obj )
387     {
388         if ( this == obj )
389         {
390             return true;
391         }
392 
393         if ( obj instanceof SinkEventAttributeSet )
394         {
395             return isEqual( (SinkEventAttributeSet) obj  );
396         }
397 
398         return false;
399     }
400 
401     /** {@inheritDoc} */
402     @Override
403     public String toString()
404     {
405         StringBuilder s = new StringBuilder();
406         Enumeration<String> names = getAttributeNames();
407 
408         while ( names.hasMoreElements() )
409         {
410             String key = names.nextElement();
411             String value = getAttribute( key ).toString();
412 
413             s.append( ' ' ).append( key ).append( '=' ).append( value );
414         }
415 
416         return s.toString();
417     }
418 
419 }