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; 020 021import javax.swing.text.AttributeSet; 022 023import java.util.Collections; 024import java.util.Enumeration; 025import java.util.LinkedHashMap; 026import java.util.Map; 027import java.util.Map.Entry; 028import java.util.Set; 029 030/** 031 * Implementation of MutableAttributeSet using a LinkedHashMap. 032 * <p> 033 * Moved here from {@code doxia-core} in 2.2.0, so that attributes can be created without depending on 034 * Doxia internals. The deprecated {@code org.apache.maven.doxia.sink.impl.SinkEventAttributeSet} remains 035 * as a subclass of this class for binary compatibility, which means the constants declared here, and every 036 * attribute set Doxia itself creates, are instances of this class and no longer of that one. 037 * 038 * @author ltheussl 039 * @since 1.1 040 */ 041public class SinkEventAttributeSet implements SinkEventAttributes, Cloneable { 042 /** 043 * An unmodifiable attribute set containing only an underline attribute. 044 */ 045 public static final SinkEventAttributes UNDERLINE; 046 047 /** 048 * An unmodifiable attribute set containing only an overline attribute. 049 */ 050 public static final SinkEventAttributes OVERLINE; 051 052 /** 053 * An unmodifiable attribute set containing only a linethrough attribute. 054 */ 055 public static final SinkEventAttributes LINETHROUGH; 056 057 /** 058 * An unmodifiable attribute set containing only a source attribute. 059 */ 060 public static final SinkEventAttributes SOURCE; 061 062 /** 063 * An unmodifiable attribute set containing only a bold attribute. 064 */ 065 public static final SinkEventAttributes BOLD; 066 067 /** 068 * An unmodifiable attribute set containing only an italic attribute. 069 */ 070 public static final SinkEventAttributes ITALIC; 071 072 /** 073 * An unmodifiable attribute set containing only a monospaced attribute. 074 */ 075 public static final SinkEventAttributes MONOSPACED; 076 077 /** 078 * An unmodifiable attribute set containing only a left attribute. 079 */ 080 public static final SinkEventAttributes LEFT; 081 082 /** 083 * An unmodifiable attribute set containing only a right attribute. 084 */ 085 public static final SinkEventAttributes RIGHT; 086 087 /** 088 * An unmodifiable attribute set containing only a center attribute. 089 */ 090 public static final SinkEventAttributes CENTER; 091 092 /** 093 * An unmodifiable attribute set containing only a justify attribute. 094 */ 095 public static final SinkEventAttributes JUSTIFY; 096 097 static { 098 UNDERLINE = new SinkEventAttributeSet(DECORATION, "underline").unmodifiable(); 099 OVERLINE = new SinkEventAttributeSet(DECORATION, "overline").unmodifiable(); 100 LINETHROUGH = new SinkEventAttributeSet(DECORATION, "line-through").unmodifiable(); 101 SOURCE = new SinkEventAttributeSet(DECORATION, "source").unmodifiable(); 102 103 BOLD = new SinkEventAttributeSet(STYLE, "bold").unmodifiable(); 104 ITALIC = new SinkEventAttributeSet(STYLE, "italic").unmodifiable(); 105 MONOSPACED = new SinkEventAttributeSet(STYLE, "monospaced").unmodifiable(); 106 107 LEFT = new SinkEventAttributeSet(ALIGN, "left").unmodifiable(); 108 RIGHT = new SinkEventAttributeSet(ALIGN, "right").unmodifiable(); 109 CENTER = new SinkEventAttributeSet(ALIGN, "center").unmodifiable(); 110 JUSTIFY = new SinkEventAttributeSet(ALIGN, "justify").unmodifiable(); 111 } 112 113 private Map<String, Object> attribs; 114 115 private AttributeSet resolveParent; 116 117 /** 118 * Constructs a new, empty SinkEventAttributeSet with default size 5. 119 */ 120 public SinkEventAttributeSet() { 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 attribs = new LinkedHashMap<>(size); 131 } 132 133 /** 134 * Constructs a new SinkEventAttributeSet with the attribute name-value 135 * mappings as given by the specified String array. 136 * Each even index of the array is an attribute name, and the following odd index is the corresponding attribute value. 137 * This constructor only supports String attribute values. 138 * 139 * @param attributes the specified String array. If the length of this array 140 * is not an even number, an IllegalArgumentException is thrown. 141 */ 142 public SinkEventAttributeSet(String... attributes) { 143 int n = attributes.length; 144 145 if ((n % 2) != 0) { 146 throw new IllegalArgumentException("Missing attribute!"); 147 } 148 149 attribs = new LinkedHashMap<>(n / 2); 150 151 for (int i = 0; i < n; i += 2) { 152 attribs.put(attributes[i], attributes[i + 1]); 153 } 154 } 155 156 /** 157 * Constructs a new SinkEventAttributeSet with the same attribute name-value 158 * mappings as in the specified AttributeSet. 159 * 160 * @param attributes the specified AttributeSet. 161 */ 162 public SinkEventAttributeSet(AttributeSet attributes) { 163 attribs = new LinkedHashMap<>(attributes.getAttributeCount()); 164 165 Enumeration<?> names = attributes.getAttributeNames(); 166 167 while (names.hasMoreElements()) { 168 Object name = names.nextElement(); 169 170 attribs.put(name.toString(), attributes.getAttribute(name)); 171 } 172 } 173 174 /** 175 * Replace this AttributeSet by an unmodifiable view of itself. 176 * Any subsequent attempt to add, remove or modify the underlying mapping 177 * will result in an UnsupportedOperationException. 178 * 179 * @return an unmodifiable view of this AttributeSet. 180 * @since 1.1.1 181 */ 182 public SinkEventAttributeSet unmodifiable() { 183 this.attribs = Collections.unmodifiableMap(attribs); 184 185 return this; 186 } 187 188 /** 189 * Checks whether the set of attribs is empty. 190 * 191 * @return true if the set is empty. 192 */ 193 public boolean isEmpty() { 194 return attribs.isEmpty(); 195 } 196 197 /** 198 * {@inheritDoc} 199 * 200 * @return a int. 201 */ 202 public int getAttributeCount() { 203 return attribs.size(); 204 } 205 206 public boolean isDefined(Object attrName) { 207 return attribs.containsKey(attrName); 208 } 209 210 public boolean isEqual(AttributeSet attr) { 211 return ((getAttributeCount() == attr.getAttributeCount()) && containsAttributes(attr)); 212 } 213 214 /** 215 * {@inheritDoc} 216 * 217 * @return a {@link javax.swing.text.AttributeSet} object. 218 */ 219 public AttributeSet copyAttributes() { 220 return ((AttributeSet) clone()); 221 } 222 223 /** 224 * {@inheritDoc} 225 * 226 * @return a {@link java.util.Enumeration} object. 227 */ 228 public Enumeration<String> getAttributeNames() { 229 return Collections.enumeration(attribs.keySet()); 230 } 231 232 public Object getAttribute(Object key) { 233 Object value = attribs.get(key); 234 235 if (value == null) { 236 AttributeSet parent = getResolveParent(); 237 238 if (parent != null) { 239 value = parent.getAttribute(key); 240 } 241 } 242 243 return value; 244 } 245 246 public boolean containsAttribute(Object name, Object value) { 247 return value.equals(getAttribute(name)); 248 } 249 250 public boolean containsAttributes(AttributeSet attributes) { 251 boolean result = true; 252 253 Enumeration<?> names = attributes.getAttributeNames(); 254 255 while (result && names.hasMoreElements()) { 256 Object name = names.nextElement(); 257 result = attributes.getAttribute(name).equals(getAttribute(name)); 258 } 259 260 return result; 261 } 262 263 /** 264 * {@inheritDoc} 265 * 266 * Adds an attribute with the given name and value. 267 */ 268 public void addAttribute(Object name, Object value) { 269 attribs.put(name.toString(), value); 270 } 271 272 public void addAttributes(AttributeSet attributes) { 273 if (attributes == null || attributes.getAttributeCount() == 0) { 274 return; 275 } 276 277 Enumeration<?> names = attributes.getAttributeNames(); 278 279 while (names.hasMoreElements()) { 280 Object name = names.nextElement(); 281 282 addAttribute(name, attributes.getAttribute(name)); 283 } 284 } 285 286 public void removeAttribute(Object name) { 287 attribs.remove(name); 288 } 289 290 public void removeAttributes(Enumeration<?> names) { 291 while (names.hasMoreElements()) { 292 removeAttribute(names.nextElement()); 293 } 294 } 295 296 /** 297 * {@inheritDoc} 298 * 299 * @param attributes a {@link javax.swing.text.AttributeSet} object. 300 */ 301 public void removeAttributes(AttributeSet attributes) { 302 if (attributes == null) { 303 return; 304 } else if (attributes == this) { 305 attribs.clear(); 306 } else { 307 Enumeration<?> names = attributes.getAttributeNames(); 308 309 while (names.hasMoreElements()) { 310 Object name = names.nextElement(); 311 Object value = attributes.getAttribute(name); 312 313 if (value.equals(getAttribute(name))) { 314 removeAttribute(name); 315 } 316 } 317 } 318 } 319 320 /** 321 * {@inheritDoc} 322 * 323 * @return a {@link javax.swing.text.AttributeSet} object. 324 */ 325 public AttributeSet getResolveParent() { 326 return this.resolveParent; 327 } 328 329 public void setResolveParent(AttributeSet parent) { 330 this.resolveParent = parent; 331 } 332 333 @Override 334 public Set<Entry<String, Object>> entrySet() { 335 return attribs.entrySet(); 336 } 337 338 @Override 339 public Object clone() { 340 // deliberately not "new SinkEventAttributeSet(...)": a subclass must clone to its own type 341 SinkEventAttributeSet attr; 342 try { 343 attr = (SinkEventAttributeSet) super.clone(); 344 } catch (CloneNotSupportedException e) { 345 throw new AssertionError(e); 346 } 347 348 attr.attribs = new LinkedHashMap<>(attribs); 349 350 if (resolveParent != null) { 351 attr.resolveParent = resolveParent.copyAttributes(); 352 } 353 354 return attr; 355 } 356 357 @Override 358 public int hashCode() { 359 return attribs.hashCode(); 360 } 361 362 @Override 363 public boolean equals(Object obj) { 364 if (this == obj) { 365 return true; 366 } 367 368 if (obj instanceof SinkEventAttributeSet) { 369 return isEqual((SinkEventAttributeSet) obj); 370 } 371 372 return false; 373 } 374 375 @Override 376 public String toString() { 377 StringBuilder s = new StringBuilder(); 378 379 for (Map.Entry<String, Object> attribute : entrySet()) { 380 s.append(' ') 381 .append(attribute.getKey()) 382 .append('=') 383 .append(attribute.getValue().toString()); 384 } 385 386 return s.toString(); 387 } 388 389 /** 390 * Attribute sets for the semantic attribute. 391 */ 392 public static class Semantics { 393 /** 394 * An unmodifiable attribute set containing only an emphasis attribute. 395 */ 396 public static final SinkEventAttributes EMPHASIS; 397 398 /** 399 * An unmodifiable attribute set containing only a strong attribute. 400 */ 401 public static final SinkEventAttributes STRONG; 402 403 /** 404 * An unmodifiable attribute set containing only a small attribute. 405 */ 406 public static final SinkEventAttributes SMALL; 407 408 /** 409 * An unmodifiable attribute set containing only a line-through attribute. 410 */ 411 public static final SinkEventAttributes LINE_THROUGH; 412 413 /** 414 * An unmodifiable attribute set containing only a citation attribute. 415 */ 416 public static final SinkEventAttributes CITATION; 417 418 /** 419 * An unmodifiable attribute set containing only a quote attribute. 420 */ 421 public static final SinkEventAttributes QUOTE; 422 423 /** 424 * An unmodifiable attribute set containing only a definition attribute. 425 */ 426 public static final SinkEventAttributes DEFINITION; 427 428 /** 429 * An unmodifiable attribute set containing only an abbreviation attribute. 430 */ 431 public static final SinkEventAttributes ABBREVIATION; 432 433 /** 434 * An unmodifiable attribute set containing only an italic attribute. 435 */ 436 public static final SinkEventAttributes ITALIC; 437 438 /** 439 * An unmodifiable attribute set containing only a bold attribute. 440 */ 441 public static final SinkEventAttributes BOLD; 442 443 /** 444 * An unmodifiable attribute set containing only a monospaced attribute. 445 */ 446 public static final SinkEventAttributes MONOSPACED; 447 448 /** 449 * An unmodifiable attribute set containing only a code attribute. 450 */ 451 public static final SinkEventAttributes CODE; 452 453 /** 454 * An unmodifiable attribute set containing only a variable attribute. 455 */ 456 public static final SinkEventAttributes VARIABLE; 457 458 /** 459 * An unmodifiable attribute set containing only a sample attribute. 460 */ 461 public static final SinkEventAttributes SAMPLE; 462 463 /** 464 * An unmodifiable attribute set containing only a keyboard attribute. 465 */ 466 public static final SinkEventAttributes KEYBOARD; 467 468 /** 469 * An unmodifiable attribute set containing only a superscript attribute. 470 */ 471 public static final SinkEventAttributes SUPERSCRIPT; 472 473 /** 474 * An unmodifiable attribute set containing only a subscript attribute. 475 */ 476 public static final SinkEventAttributes SUBSCRIPT; 477 478 /** 479 * An unmodifiable attribute set containing only an annotation attribute. 480 */ 481 public static final SinkEventAttributes ANNOTATION; 482 483 /** 484 * An unmodifiable attribute set containing only a highlight attribute. 485 */ 486 public static final SinkEventAttributes HIGHLIGHT; 487 488 /** 489 * An unmodifiable attribute set containing only a ruby attribute. 490 */ 491 public static final SinkEventAttributes RUBY; 492 493 /** 494 * An unmodifiable attribute set containing only a rubyBase attribute. 495 */ 496 public static final SinkEventAttributes RUBY_BASE; 497 498 /** 499 * An unmodifiable attribute set containing only a rubyText attribute. 500 */ 501 public static final SinkEventAttributes RUBY_TEXT; 502 503 /** 504 * An unmodifiable attribute set containing only a rubyTextContainer attribute. 505 */ 506 public static final SinkEventAttributes RUBY_TEXT_CONTAINER; 507 508 /** 509 * An unmodifiable attribute set containing only a rubyParentheses attribute. 510 */ 511 public static final SinkEventAttributes RUBY_PARANTHESES; 512 513 /** 514 * An unmodifiable attribute set containing only a bidirectionalIsolation attribute. 515 */ 516 public static final SinkEventAttributes BIDIRECTIONAL_ISOLATION; 517 518 /** 519 * An unmodifiable attribute set containing only a bidirectionalOverride attribute. 520 */ 521 public static final SinkEventAttributes BIDIRECTIONAL_OVERRIDE; 522 523 /** 524 * An unmodifiable attribute set containing only a phrase attribute. 525 */ 526 public static final SinkEventAttributes PHRASE; 527 528 /** 529 * An unmodifiable attribute set containing only an insert attribute. 530 */ 531 public static final SinkEventAttributes INSERT; 532 533 /** 534 * An unmodifiable attribute set containing only a delete attribute. 535 */ 536 public static final SinkEventAttributes DELETE; 537 538 static { 539 EMPHASIS = new SinkEventAttributeSet(SEMANTICS, "emphasis").unmodifiable(); 540 STRONG = new SinkEventAttributeSet(SEMANTICS, "strong").unmodifiable(); 541 SMALL = new SinkEventAttributeSet(SEMANTICS, "small").unmodifiable(); 542 LINE_THROUGH = new SinkEventAttributeSet(SEMANTICS, "line-through").unmodifiable(); 543 CITATION = new SinkEventAttributeSet(SEMANTICS, "citation").unmodifiable(); 544 QUOTE = new SinkEventAttributeSet(SEMANTICS, "quote").unmodifiable(); 545 DEFINITION = new SinkEventAttributeSet(SEMANTICS, "definition").unmodifiable(); 546 ABBREVIATION = new SinkEventAttributeSet(SEMANTICS, "abbreviation").unmodifiable(); 547 ITALIC = new SinkEventAttributeSet(SEMANTICS, "italic").unmodifiable(); 548 BOLD = new SinkEventAttributeSet(SEMANTICS, "bold").unmodifiable(); 549 MONOSPACED = new SinkEventAttributeSet(SEMANTICS, "monospaced").unmodifiable(); 550 CODE = new SinkEventAttributeSet(SEMANTICS, "code").unmodifiable(); 551 VARIABLE = new SinkEventAttributeSet(SEMANTICS, "variable").unmodifiable(); 552 SAMPLE = new SinkEventAttributeSet(SEMANTICS, "sample").unmodifiable(); 553 KEYBOARD = new SinkEventAttributeSet(SEMANTICS, "keyboard").unmodifiable(); 554 SUPERSCRIPT = new SinkEventAttributeSet(SEMANTICS, "superscript").unmodifiable(); 555 SUBSCRIPT = new SinkEventAttributeSet(SEMANTICS, "subscript").unmodifiable(); 556 ANNOTATION = new SinkEventAttributeSet(SEMANTICS, "annotation").unmodifiable(); 557 HIGHLIGHT = new SinkEventAttributeSet(SEMANTICS, "highlight").unmodifiable(); 558 RUBY = new SinkEventAttributeSet(SEMANTICS, "ruby").unmodifiable(); 559 RUBY_BASE = new SinkEventAttributeSet(SEMANTICS, "rubyBase").unmodifiable(); 560 RUBY_TEXT = new SinkEventAttributeSet(SEMANTICS, "rubyText").unmodifiable(); 561 RUBY_TEXT_CONTAINER = new SinkEventAttributeSet(SEMANTICS, "rubyTextContainer").unmodifiable(); 562 RUBY_PARANTHESES = new SinkEventAttributeSet(SEMANTICS, "rubyParentheses").unmodifiable(); 563 BIDIRECTIONAL_ISOLATION = new SinkEventAttributeSet(SEMANTICS, "bidirectionalIsolation").unmodifiable(); 564 BIDIRECTIONAL_OVERRIDE = new SinkEventAttributeSet(SEMANTICS, "bidirectionalOverride").unmodifiable(); 565 PHRASE = new SinkEventAttributeSet(SEMANTICS, "phrase").unmodifiable(); 566 INSERT = new SinkEventAttributeSet(SEMANTICS, "insert").unmodifiable(); 567 DELETE = new SinkEventAttributeSet(SEMANTICS, "delete").unmodifiable(); 568 } 569 } 570}