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}