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.Arrays; 024import java.util.Enumeration; 025 026import org.apache.maven.doxia.markup.Markup; 027import org.apache.maven.doxia.sink.SinkEventAttributes; 028 029/** 030 * Collection of common utility methods for sinks. 031 * 032 * @author ltheussl 033 * @since 1.1 034 */ 035public class SinkUtils { 036 037 /** Do not instantiate. */ 038 private SinkUtils() { 039 // Utility class 040 } 041 042 /** 043 * The set of base attributes. 044 */ 045 public static final String[] SINK_BASE_ATTRIBUTES = { 046 SinkEventAttributes.CLASS, 047 SinkEventAttributes.ID, 048 SinkEventAttributes.LANG, 049 SinkEventAttributes.STYLE, 050 SinkEventAttributes.TITLE 051 }; 052 053 /** 054 * The attributes that are supported for the br tag. 055 */ 056 public static final String[] SINK_BR_ATTRIBUTES = { 057 SinkEventAttributes.CLASS, SinkEventAttributes.ID, 058 SinkEventAttributes.STYLE, SinkEventAttributes.TITLE 059 }; 060 061 /** 062 * The attributes that are supported for the <img> tag. 063 */ 064 public static final String[] SINK_IMG_ATTRIBUTES; 065 066 /** 067 * The attributes that are supported for the section tags, like <p>, <h2>, <div>. 068 */ 069 public static final String[] SINK_SECTION_ATTRIBUTES; 070 071 /** 072 * The attributes that are supported for the <div> and <pre> tags. 073 */ 074 public static final String[] SINK_VERBATIM_ATTRIBUTES; 075 076 /** 077 * The attributes that are supported for the <hr> tag. 078 */ 079 public static final String[] SINK_HR_ATTRIBUTES; 080 081 /** 082 * The attributes that are supported for the <a> tag. 083 */ 084 public static final String[] SINK_LINK_ATTRIBUTES; 085 086 /** 087 * The attributes that are supported for the <table> tag. 088 */ 089 public static final String[] SINK_TABLE_ATTRIBUTES; 090 091 /** 092 * The attributes that are supported for the <td> and <th> tags. 093 */ 094 public static final String[] SINK_TD_ATTRIBUTES; 095 096 /** 097 * The attributes that are supported for the <tr> tag. 098 */ 099 public static final String[] SINK_TR_ATTRIBUTES; 100 101 private static final String[] IMG_ATTRIBUTES = { 102 SinkEventAttributes.ALT, 103 SinkEventAttributes.HEIGHT, 104 SinkEventAttributes.ISMAP, 105 SinkEventAttributes.SRC, 106 SinkEventAttributes.USEMAP, 107 SinkEventAttributes.WIDTH 108 }; 109 110 private static final String[] HR_ATTRIBUTES = {}; 111 112 private static final String[] LINK_ATTRIBUTES = { 113 SinkEventAttributes.HREF, 114 SinkEventAttributes.HREFLANG, 115 SinkEventAttributes.REL, 116 SinkEventAttributes.TARGET, 117 SinkEventAttributes.TYPE 118 }; 119 120 private static final String[] TABLE_ATTRIBUTES = {}; 121 122 private static final String[] TABLE_CELL_ATTRIBUTES = { 123 SinkEventAttributes.COLSPAN, SinkEventAttributes.HEADERS, SinkEventAttributes.ROWSPAN 124 }; 125 126 static { 127 SINK_IMG_ATTRIBUTES = join(SINK_BASE_ATTRIBUTES, IMG_ATTRIBUTES); 128 SINK_SECTION_ATTRIBUTES = join(SINK_BASE_ATTRIBUTES, new String[0]); 129 SINK_VERBATIM_ATTRIBUTES = join(SINK_BASE_ATTRIBUTES, new String[] {SinkEventAttributes.DECORATION}); 130 SINK_HR_ATTRIBUTES = join(SINK_BASE_ATTRIBUTES, HR_ATTRIBUTES); 131 SINK_LINK_ATTRIBUTES = join(SINK_BASE_ATTRIBUTES, LINK_ATTRIBUTES); 132 SINK_TABLE_ATTRIBUTES = join(SINK_BASE_ATTRIBUTES, TABLE_ATTRIBUTES); 133 SINK_TR_ATTRIBUTES = join(SINK_BASE_ATTRIBUTES, new String[0]); 134 SINK_TD_ATTRIBUTES = join(SINK_BASE_ATTRIBUTES, TABLE_CELL_ATTRIBUTES); 135 } 136 137 private static String[] join(String[] a, String[] b) { 138 String[] temp = new String[a.length + b.length]; 139 System.arraycopy(a, 0, temp, 0, a.length); 140 System.arraycopy(b, 0, temp, a.length, b.length); 141 142 Arrays.sort(temp); // necessary for binary searches in filterAttributes() 143 144 return temp; 145 } 146 147 /** 148 * Utility method to get an AttributeSet as a String. 149 * The resulting String is in the form ' name1="value1" name2="value2" ...', 150 * ie it can be appended directly to an xml start tag. Attribute values that are itself 151 * AttributeSets are ignored unless the Attribute name is SinkEventAttributeSet.STYLE, 152 * in which case they are written as outlined at 153 * {@link org.apache.maven.doxia.sink.SinkEventAttributes#STYLE SinkEventAttributes.STYLE}. 154 * All other keys and values are written as Strings. 155 * 156 * @param att The AttributeSet. May be null, in which case an empty String is returned. 157 * @return the AttributeSet as a String in a form that can be appended to an xml start tag. 158 */ 159 public static String getAttributeString(AttributeSet att) { 160 if (att == null) { 161 return ""; 162 } 163 164 StringBuilder sb = new StringBuilder(); 165 166 Enumeration<?> names = att.getAttributeNames(); 167 168 while (names.hasMoreElements()) { 169 Object key = names.nextElement(); 170 Object value = att.getAttribute(key); 171 172 if (value instanceof AttributeSet) { 173 // Other AttributeSets are ignored 174 if (SinkEventAttributes.STYLE.equals(key.toString())) { 175 sb.append(Markup.SPACE) 176 .append(key.toString()) 177 .append(Markup.EQUAL) 178 .append(Markup.QUOTE) 179 .append(asCssString((AttributeSet) value)) 180 .append(Markup.QUOTE); 181 } 182 } else { 183 sb.append(Markup.SPACE) 184 .append(key.toString()) 185 .append(Markup.EQUAL) 186 .append(Markup.QUOTE) 187 .append(value.toString()) 188 .append(Markup.QUOTE); 189 } 190 } 191 192 return sb.toString(); 193 } 194 195 public static String asCssString(AttributeSet att) { 196 StringBuilder sb = new StringBuilder(); 197 198 Enumeration<?> names = att.getAttributeNames(); 199 200 while (names.hasMoreElements()) { 201 Object key = names.nextElement(); 202 Object value = att.getAttribute(key); 203 204 // don't go recursive 205 if (!(value instanceof AttributeSet)) { 206 sb.append(asCssDeclaration(key.toString(), value.toString())); 207 208 if (names.hasMoreElements()) { 209 sb.append(Markup.SEMICOLON).append(Markup.SPACE); 210 } 211 } 212 } 213 214 return sb.toString(); 215 } 216 217 public static String asCssDeclaration(String property, String value) { 218 return property + Markup.COLON + Markup.SPACE + value; 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 {@link SinkEventAttributes} 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 SinkEventAttributes 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 SinkEventAttributes 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}