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