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.Enumeration;
23  import java.util.Arrays;
24  
25  import javax.swing.text.AttributeSet;
26  import javax.swing.text.MutableAttributeSet;
27  
28  import org.apache.maven.doxia.markup.Markup;
29  import org.apache.maven.doxia.sink.SinkEventAttributes;
30  
31  /**
32   * Collection of common utility methods for sinks.
33   *
34   * @author ltheussl
35   * @version $Id: SinkUtils.html 979316 2016-02-02 21:51:43Z hboutemy $
36   * @since 1.1
37   */
38  public class SinkUtils
39  {
40  
41      /** Do not instantiate. */
42      private SinkUtils()
43      {
44          // Utility class
45      }
46  
47      /**
48       * The set of base attributes.
49       */
50      public static final String[] SINK_BASE_ATTRIBUTES =
51      {
52          SinkEventAttributes.CLASS, SinkEventAttributes.ID, SinkEventAttributes.LANG,
53          SinkEventAttributes.STYLE, SinkEventAttributes.TITLE
54      };
55  
56      /**
57       * The attributes that are supported for the br tag.
58       */
59      public static final String[] SINK_BR_ATTRIBUTES =
60      {
61          SinkEventAttributes.CLASS, SinkEventAttributes.ID,
62          SinkEventAttributes.STYLE, SinkEventAttributes.TITLE
63      };
64  
65      /**
66       * The attributes that are supported for the <img> tag.
67       */
68      public static final String[] SINK_IMG_ATTRIBUTES;
69  
70      /**
71       * The attributes that are supported for the section tags, like <p>, <h2>, <div>.
72       */
73      public static final String[] SINK_SECTION_ATTRIBUTES;
74  
75      /**
76       * The attributes that are supported for the <div> and <pre> tags.
77       */
78      public static final String[] SINK_VERBATIM_ATTRIBUTES;
79  
80      /**
81       * The attributes that are supported for the <hr> tag.
82       */
83      public static final String[] SINK_HR_ATTRIBUTES;
84  
85      /**
86       * The attributes that are supported for the <a> tag.
87       */
88      public static final String[] SINK_LINK_ATTRIBUTES;
89  
90      /**
91       * The attributes that are supported for the <table> tag.
92       */
93      public static final String[] SINK_TABLE_ATTRIBUTES;
94  
95      /**
96       * The attributes that are supported for the <td> and <th> tags.
97       */
98      public static final String[] SINK_TD_ATTRIBUTES;
99  
100     /**
101      * The attributes that are supported for the <tr> tag.
102      */
103     public static final String[] SINK_TR_ATTRIBUTES;
104 
105     private static final String[] IMG_ATTRIBUTES =
106     {
107         SinkEventAttributes.ALIGN, SinkEventAttributes.ALT, SinkEventAttributes.BORDER,
108         SinkEventAttributes.HEIGHT, SinkEventAttributes.HSPACE, SinkEventAttributes.ISMAP,
109         SinkEventAttributes.SRC, SinkEventAttributes.USEMAP, SinkEventAttributes.VSPACE,
110         SinkEventAttributes.WIDTH
111     };
112 
113     private static final String[] HR_ATTRIBUTES =
114     {
115         SinkEventAttributes.ALIGN, SinkEventAttributes.NOSHADE, SinkEventAttributes.SIZE,
116         SinkEventAttributes.WIDTH
117     };
118 
119     private static final String[] LINK_ATTRIBUTES =
120     {
121         SinkEventAttributes.CHARSET, SinkEventAttributes.COORDS, SinkEventAttributes.HREF,
122         SinkEventAttributes.HREFLANG, SinkEventAttributes.REL, SinkEventAttributes.REV,
123         SinkEventAttributes.SHAPE, SinkEventAttributes.TARGET, SinkEventAttributes.TYPE
124     };
125 
126     private static final String[] TABLE_ATTRIBUTES =
127     {
128         SinkEventAttributes.ALIGN, SinkEventAttributes.BGCOLOR, SinkEventAttributes.BORDER,
129         SinkEventAttributes.CELLPADDING, SinkEventAttributes.CELLSPACING, SinkEventAttributes.FRAME,
130         SinkEventAttributes.RULES, SinkEventAttributes.SUMMARY, SinkEventAttributes.WIDTH
131     };
132 
133     private static final String[] TABLE_CELL_ATTRIBUTES =
134     {
135         SinkEventAttributes.ABBRV, SinkEventAttributes.ALIGN, SinkEventAttributes.AXIS,
136         SinkEventAttributes.BGCOLOR, SinkEventAttributes.COLSPAN, SinkEventAttributes.HEADERS,
137         SinkEventAttributes.HEIGHT, SinkEventAttributes.NOWRAP, SinkEventAttributes.ROWSPAN,
138         SinkEventAttributes.SCOPE, SinkEventAttributes.VALIGN, SinkEventAttributes.WIDTH
139     };
140 
141     static
142     {
143         SINK_IMG_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, IMG_ATTRIBUTES );
144         SINK_SECTION_ATTRIBUTES =
145                 join( SINK_BASE_ATTRIBUTES, new String[] {SinkEventAttributes.ALIGN} );
146         SINK_VERBATIM_ATTRIBUTES =
147                 join( SINK_BASE_ATTRIBUTES,
148                 new String[] {SinkEventAttributes.ALIGN, SinkEventAttributes.DECORATION, SinkEventAttributes.WIDTH} );
149         SINK_HR_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, HR_ATTRIBUTES );
150         SINK_LINK_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, LINK_ATTRIBUTES );
151         SINK_TABLE_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, TABLE_ATTRIBUTES );
152         SINK_TR_ATTRIBUTES =
153                 join( SINK_BASE_ATTRIBUTES,
154                 new String[] {SinkEventAttributes.ALIGN, SinkEventAttributes.BGCOLOR, SinkEventAttributes.VALIGN} );
155         SINK_TD_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, TABLE_CELL_ATTRIBUTES );
156     }
157 
158     private static String[] join( String[] a, String[] b )
159     {
160         String[] temp = new String[a.length + b.length];
161         System.arraycopy( a, 0, temp, 0, a.length );
162         System.arraycopy( b, 0, temp, a.length, b.length );
163 
164         Arrays.sort( temp ); // necessary for binary searches in filterAttributes()
165 
166         return temp;
167     }
168 
169     /**
170      * Utility method to get an AttributeSet as a String.
171      * The resulting String is in the form ' name1="value1" name2="value2" ...',
172      * ie it can be appended directly to an xml start tag. Attribute values that are itself
173      * AttributeSets are ignored unless the Attribute name is SinkEventAttributeSet.STYLE,
174      * in which case they are written as outlined at
175      * {@link org.apache.maven.doxia.sink.SinkEventAttributes#STYLE SinkEventAttributes.STYLE}.
176      * All other keys and values are written as Strings.
177      *
178      * @param att The AttributeSet. May be null, in which case an empty String is returned.
179      * @return the AttributeSet as a String in a form that can be appended to an xml start tag.
180      */
181     public static String getAttributeString( AttributeSet att )
182     {
183         if ( att == null )
184         {
185             return "";
186         }
187 
188         StringBuilder sb = new StringBuilder();
189 
190         Enumeration<?> names = att.getAttributeNames();
191 
192         while ( names.hasMoreElements() )
193         {
194             Object key = names.nextElement();
195             Object value = att.getAttribute( key );
196 
197             if ( value instanceof AttributeSet )
198             {
199                 // Other AttributeSets are ignored
200                 if ( SinkEventAttributes.STYLE.equals( key.toString() ) )
201                 {
202                     sb.append( Markup.SPACE ).append( key.toString() ).append( Markup.EQUAL )
203                         .append( Markup.QUOTE ).append( asCssString( (AttributeSet) value ) )
204                         .append( Markup.QUOTE );
205                 }
206             }
207             else
208             {
209                 sb.append( Markup.SPACE ).append( key.toString() ).append( Markup.EQUAL )
210                     .append( Markup.QUOTE ).append( value.toString() ).append( Markup.QUOTE );
211             }
212         }
213 
214         return sb.toString();
215     }
216 
217     private static String asCssString( AttributeSet att )
218     {
219         StringBuilder sb = new StringBuilder();
220 
221         Enumeration<?> names = att.getAttributeNames();
222 
223         while ( names.hasMoreElements() )
224         {
225             Object key = names.nextElement();
226             Object value = att.getAttribute( key );
227 
228             // don't go recursive
229             if ( !( value instanceof AttributeSet ) )
230             {
231                 sb.append( key.toString() ).append( Markup.COLON )
232                     .append( Markup.SPACE ).append( value.toString() );
233 
234                 if ( names.hasMoreElements() )
235                 {
236                     sb.append( Markup.SEMICOLON ).append( Markup.SPACE );
237                 }
238             }
239         }
240 
241         return sb.toString();
242     }
243 
244     /**
245      * Filters the given AttributeSet.
246      * Removes all attributes whose name (key) is not contained in the sorted array valids.
247      *
248      * @param attributes The AttributeSet to filter. The String values of Attribute names
249      * are compared to the elements of the valids array.
250      * @param valids a sorted array of attribute names that are to be kept in the resulting AttributeSet.
251      *      <b>Note:</b> a binary search is employed, so the array has to be sorted for correct results.
252      * @return A filtered MutableAttributeSet object. Returns null if the input AttributeSet is null.
253      *      If the array of valids is either null or empty, an empty AttributeSet is returned.
254      */
255     public static MutableAttributeSet filterAttributes( AttributeSet attributes, String[] valids )
256     {
257         if ( attributes == null )
258         {
259             return null;
260         }
261 
262         if ( valids == null || valids.length == 0 )
263         {
264             return new SinkEventAttributeSet( 0 );
265         }
266 
267         MutableAttributeSet atts = new SinkEventAttributeSet( attributes.getAttributeCount() );
268 
269         Enumeration<?> names = attributes.getAttributeNames();
270 
271         while ( names.hasMoreElements() )
272         {
273             String key = names.nextElement().toString();
274 
275             if ( Arrays.binarySearch( valids, key ) >= 0 )
276             {
277                 atts.addAttribute( key, attributes.getAttribute( key ) );
278             }
279         }
280 
281         return atts;
282     }
283 }