001package org.apache.maven.doxia.sink; 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.Collections; 023import java.util.Enumeration; 024import java.util.LinkedHashMap; 025import java.util.Map; 026 027import javax.swing.text.AttributeSet; 028 029/** 030 * Implementation of MutableAttributeSet using a LinkedHashMap. 031 * 032 * @author ltheussl 033 * @version $Id$ 034 * @since 1.1 035 */ 036public class SinkEventAttributeSet 037 implements SinkEventAttributes, Cloneable 038{ 039 /** 040 * An unmodifiable attribute set containing only an underline attribute. 041 */ 042 public static final SinkEventAttributes UNDERLINE; 043 044 /** 045 * An unmodifiable attribute set containing only an overline attribute. 046 */ 047 public static final SinkEventAttributes OVERLINE; 048 049 /** 050 * An unmodifiable attribute set containing only a linethrough attribute. 051 */ 052 public static final SinkEventAttributes LINETHROUGH; 053 054 /** 055 * An unmodifiable attribute set containing only a boxed attribute. 056 */ 057 public static final SinkEventAttributes BOXED; 058 059 /** 060 * An unmodifiable attribute set containing only a bold attribute. 061 */ 062 public static final SinkEventAttributes BOLD; 063 064 /** 065 * An unmodifiable attribute set containing only an italic attribute. 066 */ 067 public static final SinkEventAttributes ITALIC; 068 069 /** 070 * An unmodifiable attribute set containing only a monospaced attribute. 071 */ 072 public static final SinkEventAttributes MONOSPACED; 073 074 /** 075 * An unmodifiable attribute set containing only a left attribute. 076 */ 077 public static final SinkEventAttributes LEFT; 078 079 /** 080 * An unmodifiable attribute set containing only a right attribute. 081 */ 082 public static final SinkEventAttributes RIGHT; 083 084 /** 085 * An unmodifiable attribute set containing only a center attribute. 086 */ 087 public static final SinkEventAttributes CENTER; 088 089 /** 090 * An unmodifiable attribute set containing only a justify attribute. 091 */ 092 public static final SinkEventAttributes JUSTIFY; 093 094 095 static 096 { 097 UNDERLINE = new SinkEventAttributeSet( new String[] {DECORATION, "underline"} ).unmodifiable(); 098 OVERLINE = new SinkEventAttributeSet( new String[] {DECORATION, "overline"} ).unmodifiable(); 099 LINETHROUGH = new SinkEventAttributeSet( new String[] {DECORATION, "line-through"} ).unmodifiable(); 100 BOXED = new SinkEventAttributeSet( new String[] {DECORATION, "boxed"} ).unmodifiable(); 101 102 BOLD = new SinkEventAttributeSet( new String[] {STYLE, "bold"} ).unmodifiable(); 103 ITALIC = new SinkEventAttributeSet( new String[] {STYLE, "italic"} ).unmodifiable(); 104 MONOSPACED = new SinkEventAttributeSet( new String[] {STYLE, "monospaced"} ).unmodifiable(); 105 106 LEFT = new SinkEventAttributeSet( new String[] {ALIGN, "left"} ).unmodifiable(); 107 RIGHT = new SinkEventAttributeSet( new String[] {ALIGN, "right"} ).unmodifiable(); 108 CENTER = new SinkEventAttributeSet( new String[] {ALIGN, "center"} ).unmodifiable(); 109 JUSTIFY = new SinkEventAttributeSet( new String[] {ALIGN, "justify"} ).unmodifiable(); 110 } 111 112 private Map<String, Object> attribs; 113 114 private AttributeSet resolveParent; 115 116 /** 117 * Constructs a new, empty SinkEventAttributeSet with default size 5. 118 */ 119 public SinkEventAttributeSet() 120 { 121 this( 5 ); 122 } 123 124 /** 125 * Constructs a new, empty SinkEventAttributeSet with the specified initial size. 126 * 127 * @param size the initial number of attribs. 128 */ 129 public SinkEventAttributeSet( int size ) 130 { 131 attribs = new LinkedHashMap<String, Object>( size ); 132 } 133 134 /** 135 * Constructs a new SinkEventAttributeSet with the attribute name-value 136 * mappings as given by the specified String array. 137 * 138 * @param attributes the specified String array. If the length of this array 139 * is not an even number, an IllegalArgumentException is thrown. 140 */ 141 public SinkEventAttributeSet( String... attributes ) 142 { 143 int n = attributes.length; 144 145 if ( ( n % 2 ) != 0 ) 146 { 147 throw new IllegalArgumentException( "Missing attribute!" ); 148 } 149 150 attribs = new LinkedHashMap<String, Object>( n / 2 ); 151 152 for ( int i = 0; i < n; i += 2 ) 153 { 154 attribs.put( attributes[i], attributes[i + 1] ); 155 } 156 } 157 158 /** 159 * Constructs a new SinkEventAttributeSet with the same attribute name-value 160 * mappings as in the specified AttributeSet. 161 * 162 * @param attributes the specified AttributeSet. 163 */ 164 public SinkEventAttributeSet( AttributeSet attributes ) 165 { 166 attribs = new LinkedHashMap<String, Object>( attributes.getAttributeCount() ); 167 168 Enumeration<?> names = attributes.getAttributeNames(); 169 170 while ( names.hasMoreElements() ) 171 { 172 Object name = names.nextElement(); 173 174 attribs.put( name.toString(), attributes.getAttribute( name ) ); 175 } 176 } 177 178 /** 179 * Replace this AttributeSet by an unmodifiable view of itself. 180 * Any subsequent attempt to add, remove or modify the underlying mapping 181 * will result in an UnsupportedOperationException. 182 * 183 * @return an unmodifiable view of this AttributeSet. 184 * 185 * @since 1.1.1 186 */ 187 public SinkEventAttributeSet unmodifiable() 188 { 189 this.attribs = Collections.unmodifiableMap( attribs ); 190 191 return this; 192 } 193 194 /** 195 * Checks whether the set of attribs is empty. 196 * 197 * @return true if the set is empty. 198 */ 199 public boolean isEmpty() 200 { 201 return attribs.isEmpty(); 202 } 203 204 /** {@inheritDoc} */ 205 public int getAttributeCount() 206 { 207 return attribs.size(); 208 } 209 210 /** {@inheritDoc} */ 211 public boolean isDefined( Object attrName ) 212 { 213 return attribs.containsKey( attrName ); 214 } 215 216 /** {@inheritDoc} */ 217 public boolean isEqual( AttributeSet attr ) 218 { 219 return ( ( getAttributeCount() == attr.getAttributeCount() ) 220 && containsAttributes( attr ) ); 221 } 222 223 /** {@inheritDoc} */ 224 public AttributeSet copyAttributes() 225 { 226 return ( (AttributeSet) clone() ); 227 } 228 229 /** {@inheritDoc} */ 230 public Enumeration<String> getAttributeNames() 231 { 232 return Collections.enumeration( attribs.keySet() ); 233 } 234 235 /** {@inheritDoc} */ 236 public Object getAttribute( Object key ) 237 { 238 Object value = attribs.get( key ); 239 240 if ( value == null ) 241 { 242 AttributeSet parent = getResolveParent(); 243 244 if ( parent != null ) 245 { 246 value = parent.getAttribute( key ); 247 } 248 } 249 250 return value; 251 } 252 253 /** {@inheritDoc} */ 254 public boolean containsAttribute( Object name, Object value ) 255 { 256 return value.equals( getAttribute( name ) ); 257 } 258 259 /** {@inheritDoc} */ 260 public boolean containsAttributes( AttributeSet attributes ) 261 { 262 boolean result = true; 263 264 Enumeration<?> names = attributes.getAttributeNames(); 265 266 while ( result && names.hasMoreElements() ) 267 { 268 Object name = names.nextElement(); 269 result = attributes.getAttribute( name ).equals( getAttribute( name ) ); 270 } 271 272 return result; 273 } 274 275 /** 276 * {@inheritDoc} 277 * 278 * Adds an attribute with the given name and value. 279 */ 280 public void addAttribute( Object name, Object value ) 281 { 282 attribs.put( name.toString(), value ); 283 } 284 285 /** {@inheritDoc} */ 286 public void addAttributes( AttributeSet attributes ) 287 { 288 if ( attributes == null || attributes.getAttributeCount() == 0 ) 289 { 290 return; 291 } 292 293 Enumeration<?> names = attributes.getAttributeNames(); 294 295 while ( names.hasMoreElements() ) 296 { 297 Object name = names.nextElement(); 298 299 addAttribute( name, attributes.getAttribute( name ) ); 300 } 301 } 302 303 /** {@inheritDoc} */ 304 public void removeAttribute( Object name ) 305 { 306 attribs.remove( name ); 307 } 308 309 /** {@inheritDoc} */ 310 public void removeAttributes( Enumeration<?> names ) 311 { 312 while ( names.hasMoreElements() ) 313 { 314 removeAttribute( names.nextElement() ); 315 } 316 } 317 318 /** {@inheritDoc} */ 319 public void removeAttributes( AttributeSet attributes ) 320 { 321 if ( attributes == null ) 322 { 323 return; 324 } 325 else if ( attributes == this ) 326 { 327 attribs.clear(); 328 } 329 else 330 { 331 Enumeration<?> names = attributes.getAttributeNames(); 332 333 while ( names.hasMoreElements() ) 334 { 335 Object name = names.nextElement(); 336 Object value = attributes.getAttribute( name ); 337 338 if ( value.equals( getAttribute( name ) ) ) 339 { 340 removeAttribute( name ); 341 } 342 } 343 } 344 } 345 346 /** {@inheritDoc} */ 347 public AttributeSet getResolveParent() 348 { 349 return this.resolveParent; 350 } 351 352 /** {@inheritDoc} */ 353 public void setResolveParent( AttributeSet parent ) 354 { 355 this.resolveParent = parent; 356 } 357 358 /** {@inheritDoc} */ 359 @Override 360 public Object clone() 361 { 362 SinkEventAttributeSet attr = new SinkEventAttributeSet( attribs.size() ); 363 attr.attribs = new LinkedHashMap<String, Object>( attribs ); 364 365 if ( resolveParent != null ) 366 { 367 attr.resolveParent = resolveParent.copyAttributes(); 368 } 369 370 return attr; 371 } 372 373 /** {@inheritDoc} */ 374 @Override 375 public int hashCode() 376 { 377 final int parentHash = ( resolveParent == null ? 0 : resolveParent.hashCode() ); 378 379 return attribs.hashCode() + parentHash; 380 } 381 382 /** {@inheritDoc} */ 383 @Override 384 public boolean equals( Object obj ) 385 { 386 if ( this == obj ) 387 { 388 return true; 389 } 390 391 if ( obj instanceof SinkEventAttributeSet ) 392 { 393 return isEqual( (SinkEventAttributeSet) obj ); 394 } 395 396 return false; 397 } 398 399 /** {@inheritDoc} */ 400 @Override 401 public String toString() 402 { 403 StringBuilder s = new StringBuilder(); 404 Enumeration<String> names = getAttributeNames(); 405 406 while ( names.hasMoreElements() ) 407 { 408 String key = names.nextElement(); 409 String value = getAttribute( key ).toString(); 410 411 s.append( ' ' ).append( key ).append( '=' ).append( value ); 412 } 413 414 return s.toString(); 415 } 416 417}