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