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