001package org.apache.maven.doxia.sink.impl;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.Collections;
023import java.util.Enumeration;
024import java.util.LinkedHashMap;
025import java.util.Map;
026
027import javax.swing.text.AttributeSet;
028
029import org.apache.maven.doxia.sink.SinkEventAttributes;
030
031/**
032 * Implementation of MutableAttributeSet using a LinkedHashMap.
033 *
034 * @author ltheussl
035 * @since 1.1
036 */
037public class SinkEventAttributeSet
038    implements SinkEventAttributes, Cloneable
039{
040    /**
041     * An unmodifiable attribute set containing only an underline attribute.
042     */
043    public static final SinkEventAttributes UNDERLINE;
044
045    /**
046     * An unmodifiable attribute set containing only an overline attribute.
047     */
048    public static final SinkEventAttributes OVERLINE;
049
050    /**
051     * An unmodifiable attribute set containing only a linethrough attribute.
052     */
053    public static final SinkEventAttributes LINETHROUGH;
054
055    /**
056     * An unmodifiable attribute set containing only a boxed attribute.
057     */
058    public static final SinkEventAttributes BOXED;
059
060    /**
061     * An unmodifiable attribute set containing only a bold attribute.
062     */
063    public static final SinkEventAttributes BOLD;
064
065    /**
066     * An unmodifiable attribute set containing only an italic attribute.
067     */
068    public static final SinkEventAttributes ITALIC;
069
070    /**
071     * An unmodifiable attribute set containing only a monospaced attribute.
072     */
073    public static final SinkEventAttributes MONOSPACED;
074
075    /**
076     * An unmodifiable attribute set containing only a left attribute.
077     */
078    public static final SinkEventAttributes LEFT;
079
080    /**
081     * An unmodifiable attribute set containing only a right attribute.
082     */
083    public static final SinkEventAttributes RIGHT;
084
085    /**
086     * An unmodifiable attribute set containing only a center attribute.
087     */
088    public static final SinkEventAttributes CENTER;
089
090    /**
091     * An unmodifiable attribute set containing only a justify attribute.
092     */
093    public static final SinkEventAttributes JUSTIFY;
094
095
096    static
097    {
098        UNDERLINE = new SinkEventAttributeSet( DECORATION, "underline" ).unmodifiable();
099        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}