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; 022import javax.swing.text.MutableAttributeSet; 023 024import java.util.Arrays; 025import java.util.Enumeration; 026 027import org.apache.maven.doxia.markup.Markup; 028import org.apache.maven.doxia.sink.SinkEventAttributes; 029 030/** 031 * Collection of common utility methods for sinks. 032 * 033 * @author ltheussl 034 * @since 1.1 035 */ 036public class SinkUtils { 037 038 /** Do not instantiate. */ 039 private SinkUtils() { 040 // Utility class 041 } 042 043 /** 044 * The set of base attributes. 045 */ 046 public static final String[] SINK_BASE_ATTRIBUTES = { 047 SinkEventAttributes.CLASS, 048 SinkEventAttributes.ID, 049 SinkEventAttributes.LANG, 050 SinkEventAttributes.STYLE, 051 SinkEventAttributes.TITLE 052 }; 053 054 /** 055 * The attributes that are supported for the br tag. 056 */ 057 public static final String[] SINK_BR_ATTRIBUTES = { 058 SinkEventAttributes.CLASS, SinkEventAttributes.ID, 059 SinkEventAttributes.STYLE, SinkEventAttributes.TITLE 060 }; 061 062 /** 063 * The attributes that are supported for the <img> tag. 064 */ 065 public static final String[] SINK_IMG_ATTRIBUTES; 066 067 /** 068 * The attributes that are supported for the section tags, like <p>, <h2>, <div>. 069 */ 070 public static final String[] SINK_SECTION_ATTRIBUTES; 071 072 /** 073 * The attributes that are supported for the <div> and <pre> tags. 074 */ 075 public static final String[] SINK_VERBATIM_ATTRIBUTES; 076 077 /** 078 * The attributes that are supported for the <hr> tag. 079 */ 080 public static final String[] SINK_HR_ATTRIBUTES; 081 082 /** 083 * The attributes that are supported for the <a> tag. 084 */ 085 public static final String[] SINK_LINK_ATTRIBUTES; 086 087 /** 088 * The attributes that are supported for the <table> tag. 089 */ 090 public static final String[] SINK_TABLE_ATTRIBUTES; 091 092 /** 093 * The attributes that are supported for the <td> and <th> tags. 094 */ 095 public static final String[] SINK_TD_ATTRIBUTES; 096 097 /** 098 * The attributes that are supported for the <tr> tag. 099 */ 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}