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.Enumeration; 023import java.util.Arrays; 024 025import javax.swing.text.AttributeSet; 026import javax.swing.text.MutableAttributeSet; 027 028import org.apache.maven.doxia.markup.Markup; 029import org.apache.maven.doxia.sink.SinkEventAttributes; 030 031/** 032 * Collection of common utility methods for sinks. 033 * 034 * @author ltheussl 035 * @since 1.1 036 */ 037public class SinkUtils 038{ 039 040 /** Do not instantiate. */ 041 private SinkUtils() 042 { 043 // Utility class 044 } 045 046 /** 047 * The set of base attributes. 048 */ 049 public static final String[] SINK_BASE_ATTRIBUTES = 050 { 051 SinkEventAttributes.CLASS, SinkEventAttributes.ID, SinkEventAttributes.LANG, 052 SinkEventAttributes.STYLE, SinkEventAttributes.TITLE 053 }; 054 055 /** 056 * The attributes that are supported for the br tag. 057 */ 058 public static final String[] SINK_BR_ATTRIBUTES = 059 { 060 SinkEventAttributes.CLASS, SinkEventAttributes.ID, 061 SinkEventAttributes.STYLE, SinkEventAttributes.TITLE 062 }; 063 064 /** 065 * The attributes that are supported for the <img> tag. 066 */ 067 public static final String[] SINK_IMG_ATTRIBUTES; 068 069 /** 070 * The attributes that are supported for the section tags, like <p>, <h2>, <div>. 071 */ 072 public static final String[] SINK_SECTION_ATTRIBUTES; 073 074 /** 075 * The attributes that are supported for the <div> and <pre> tags. 076 */ 077 public static final String[] SINK_VERBATIM_ATTRIBUTES; 078 079 /** 080 * The attributes that are supported for the <hr> tag. 081 */ 082 public static final String[] SINK_HR_ATTRIBUTES; 083 084 /** 085 * The attributes that are supported for the <a> tag. 086 */ 087 public static final String[] SINK_LINK_ATTRIBUTES; 088 089 /** 090 * The attributes that are supported for the <table> tag. 091 */ 092 public static final String[] SINK_TABLE_ATTRIBUTES; 093 094 /** 095 * The attributes that are supported for the <td> and <th> tags. 096 */ 097 public static final String[] SINK_TD_ATTRIBUTES; 098 099 /** 100 * The attributes that are supported for the <tr> tag. 101 */ 102 public static final String[] SINK_TR_ATTRIBUTES; 103 104 private static final String[] IMG_ATTRIBUTES = 105 { 106 SinkEventAttributes.ALIGN, SinkEventAttributes.ALT, SinkEventAttributes.BORDER, 107 SinkEventAttributes.HEIGHT, SinkEventAttributes.HSPACE, SinkEventAttributes.ISMAP, 108 SinkEventAttributes.SRC, SinkEventAttributes.USEMAP, SinkEventAttributes.VSPACE, 109 SinkEventAttributes.WIDTH 110 }; 111 112 private static final String[] HR_ATTRIBUTES = 113 { 114 SinkEventAttributes.ALIGN, SinkEventAttributes.NOSHADE, SinkEventAttributes.SIZE, 115 SinkEventAttributes.WIDTH 116 }; 117 118 private static final String[] LINK_ATTRIBUTES = 119 { 120 SinkEventAttributes.CHARSET, SinkEventAttributes.COORDS, SinkEventAttributes.HREF, 121 SinkEventAttributes.HREFLANG, SinkEventAttributes.REL, SinkEventAttributes.REV, 122 SinkEventAttributes.SHAPE, SinkEventAttributes.TARGET, SinkEventAttributes.TYPE 123 }; 124 125 private static final String[] TABLE_ATTRIBUTES = 126 { 127 SinkEventAttributes.ALIGN, SinkEventAttributes.BGCOLOR, SinkEventAttributes.BORDER, 128 SinkEventAttributes.CELLPADDING, SinkEventAttributes.CELLSPACING, SinkEventAttributes.FRAME, 129 SinkEventAttributes.RULES, SinkEventAttributes.SUMMARY, SinkEventAttributes.WIDTH 130 }; 131 132 private static final String[] TABLE_CELL_ATTRIBUTES = 133 { 134 SinkEventAttributes.ABBRV, SinkEventAttributes.ALIGN, SinkEventAttributes.AXIS, 135 SinkEventAttributes.BGCOLOR, SinkEventAttributes.COLSPAN, SinkEventAttributes.HEADERS, 136 SinkEventAttributes.HEIGHT, SinkEventAttributes.NOWRAP, SinkEventAttributes.ROWSPAN, 137 SinkEventAttributes.SCOPE, SinkEventAttributes.VALIGN, SinkEventAttributes.WIDTH 138 }; 139 140 static 141 { 142 SINK_IMG_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, IMG_ATTRIBUTES ); 143 SINK_SECTION_ATTRIBUTES = 144 join( SINK_BASE_ATTRIBUTES, new String[] {SinkEventAttributes.ALIGN} ); 145 SINK_VERBATIM_ATTRIBUTES = 146 join( SINK_BASE_ATTRIBUTES, 147 new String[] {SinkEventAttributes.ALIGN, SinkEventAttributes.DECORATION, SinkEventAttributes.WIDTH} ); 148 SINK_HR_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, HR_ATTRIBUTES ); 149 SINK_LINK_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, LINK_ATTRIBUTES ); 150 SINK_TABLE_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, TABLE_ATTRIBUTES ); 151 SINK_TR_ATTRIBUTES = 152 join( SINK_BASE_ATTRIBUTES, 153 new String[] {SinkEventAttributes.ALIGN, SinkEventAttributes.BGCOLOR, SinkEventAttributes.VALIGN} ); 154 SINK_TD_ATTRIBUTES = join( SINK_BASE_ATTRIBUTES, TABLE_CELL_ATTRIBUTES ); 155 } 156 157 private static String[] join( String[] a, String[] b ) 158 { 159 String[] temp = new String[a.length + b.length]; 160 System.arraycopy( a, 0, temp, 0, a.length ); 161 System.arraycopy( b, 0, temp, a.length, b.length ); 162 163 Arrays.sort( temp ); // necessary for binary searches in filterAttributes() 164 165 return temp; 166 } 167 168 /** 169 * Utility method to get an AttributeSet as a String. 170 * The resulting String is in the form ' name1="value1" name2="value2" ...', 171 * ie it can be appended directly to an xml start tag. Attribute values that are itself 172 * AttributeSets are ignored unless the Attribute name is SinkEventAttributeSet.STYLE, 173 * in which case they are written as outlined at 174 * {@link org.apache.maven.doxia.sink.SinkEventAttributes#STYLE SinkEventAttributes.STYLE}. 175 * All other keys and values are written as Strings. 176 * 177 * @param att The AttributeSet. May be null, in which case an empty String is returned. 178 * @return the AttributeSet as a String in a form that can be appended to an xml start tag. 179 */ 180 public static String getAttributeString( AttributeSet att ) 181 { 182 if ( att == null ) 183 { 184 return ""; 185 } 186 187 StringBuilder sb = new StringBuilder(); 188 189 Enumeration<?> names = att.getAttributeNames(); 190 191 while ( names.hasMoreElements() ) 192 { 193 Object key = names.nextElement(); 194 Object value = att.getAttribute( key ); 195 196 if ( value instanceof AttributeSet ) 197 { 198 // Other AttributeSets are ignored 199 if ( SinkEventAttributes.STYLE.equals( key.toString() ) ) 200 { 201 sb.append( Markup.SPACE ).append( key.toString() ).append( Markup.EQUAL ) 202 .append( Markup.QUOTE ).append( asCssString( (AttributeSet) value ) ) 203 .append( Markup.QUOTE ); 204 } 205 } 206 else 207 { 208 sb.append( Markup.SPACE ).append( key.toString() ).append( Markup.EQUAL ) 209 .append( Markup.QUOTE ).append( value.toString() ).append( Markup.QUOTE ); 210 } 211 } 212 213 return sb.toString(); 214 } 215 216 private static String asCssString( AttributeSet att ) 217 { 218 StringBuilder sb = new StringBuilder(); 219 220 Enumeration<?> names = att.getAttributeNames(); 221 222 while ( names.hasMoreElements() ) 223 { 224 Object key = names.nextElement(); 225 Object value = att.getAttribute( key ); 226 227 // don't go recursive 228 if ( !( value instanceof AttributeSet ) ) 229 { 230 sb.append( key.toString() ).append( Markup.COLON ) 231 .append( Markup.SPACE ).append( value.toString() ); 232 233 if ( names.hasMoreElements() ) 234 { 235 sb.append( Markup.SEMICOLON ).append( Markup.SPACE ); 236 } 237 } 238 } 239 240 return sb.toString(); 241 } 242 243 /** 244 * Filters the given AttributeSet. 245 * Removes all attributes whose name (key) is not contained in the sorted array valids. 246 * 247 * @param attributes The AttributeSet to filter. The String values of Attribute names 248 * are compared to the elements of the valids array. 249 * @param valids a sorted array of attribute names that are to be kept in the resulting AttributeSet. 250 * <b>Note:</b> a binary search is employed, so the array has to be sorted for correct results. 251 * @return A filtered MutableAttributeSet object. Returns null if the input AttributeSet is null. 252 * If the array of valids is either null or empty, an empty AttributeSet is returned. 253 */ 254 public static MutableAttributeSet filterAttributes( AttributeSet attributes, String[] valids ) 255 { 256 if ( attributes == null ) 257 { 258 return null; 259 } 260 261 if ( valids == null || valids.length == 0 ) 262 { 263 return new SinkEventAttributeSet( 0 ); 264 } 265 266 MutableAttributeSet atts = new SinkEventAttributeSet( attributes.getAttributeCount() ); 267 268 Enumeration<?> names = attributes.getAttributeNames(); 269 270 while ( names.hasMoreElements() ) 271 { 272 String key = names.nextElement().toString(); 273 274 if ( Arrays.binarySearch( valids, key ) >= 0 ) 275 { 276 atts.addAttribute( key, attributes.getAttribute( key ) ); 277 } 278 } 279 280 return atts; 281 } 282}