1 /* 2 * #%L 3 * wcm.io 4 * %% 5 * Copyright (C) 2014 wcm.io 6 * %% 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * #L% 19 */ 20 package io.wcm.handler.commons.dom; 21 22 import java.util.Collection; 23 import java.util.regex.Pattern; 24 25 import org.apache.commons.lang3.StringUtils; 26 import org.jdom2.Attribute; 27 import org.jdom2.Content; 28 import org.jdom2.DataConversionException; 29 import org.jdom2.Namespace; 30 import org.jdom2.output.XMLOutputter; 31 import org.osgi.annotation.versioning.ConsumerType; 32 33 /** 34 * Generic DOM element. 35 * This element implementation is derived from JDOM element implementation. 36 */ 37 @SuppressWarnings("unchecked") 38 @ConsumerType 39 public abstract class AbstractElement extends org.jdom2.Element { 40 41 private static final long serialVersionUID = 1L; 42 43 // matches all control chars ([\x00-\x1F\x7F]), that are invalid inside XML 44 private static final Pattern CONTROL_CHARS = Pattern.compile("[\\p{Cntrl}]"); 45 46 /** 47 * Initializes DOM element. 48 * @param name Element name 49 */ 50 protected AbstractElement(String name) { 51 super(name); 52 } 53 54 /** 55 * Gets attribute value as integer. 56 * @param attributeName Attribute name 57 * @return Attribute value as integer or 0 if not set. 58 */ 59 public final int getAttributeValueAsInteger(String attributeName) { 60 Attribute attribute = getAttribute(attributeName); 61 if (attribute == null) { 62 return 0; 63 } 64 else { 65 try { 66 return attribute.getIntValue(); 67 } 68 catch (DataConversionException ex) { 69 return 0; 70 } 71 } 72 } 73 74 /** 75 * Sets attribute value as long. 76 * @param name Attribute name 77 * @param value Attribute value as long 78 * @return Self reference 79 */ 80 public AbstractElement setAttributeValueAsLong(String name, long value) { 81 setAttribute(name, Long.toString(value)); 82 return this; 83 } 84 85 /** 86 * Gets attribute value as long. 87 * @param attributeName Attribute name 88 * @return Attribute value as long or 0 if not set. 89 */ 90 public final long getAttributeValueAsLong(String attributeName) { 91 Attribute attribute = getAttribute(attributeName); 92 if (attribute == null) { 93 return 0; 94 } 95 else { 96 try { 97 return attribute.getLongValue(); 98 } 99 catch (DataConversionException ex) { 100 return 0; 101 } 102 } 103 } 104 105 /** 106 * Sets attribute value as integer. 107 * @param name Attribute name 108 * @param value Attribute value as integer 109 * @return Self reference 110 */ 111 public final AbstractElement setAttributeValueAsInteger(String name, int value) { 112 setAttribute(name, Integer.toString(value)); 113 return this; 114 } 115 116 /** 117 * Appends the child to the end of the element's content list 118 * @param content Child to append to end of content list. Null values are ignored. 119 * @return The element on which the method was called. 120 */ 121 @Override 122 public final org.jdom2.Element addContent(Content content) { 123 // ignore empty elements 124 if (content == null) { 125 return null; 126 } 127 return super.addContent(content); 128 } 129 130 /** 131 * Inserts the child into the content list at the given index 132 * @param index Location for adding the collection 133 * @param content Child to append to end of content list. Null values are ignored. 134 * @return The element on which the method was called. 135 */ 136 @Override 137 public final org.jdom2.Element addContent(int index, Content content) { 138 // ignore empty elements 139 if (content == null) { 140 return null; 141 } 142 return super.addContent(index, content); 143 } 144 145 /** 146 * This adds text content to this element. It does not replace the existing content as does setText(). 147 * @param text String to add. Null values are ignored. 148 * @return This element modified 149 */ 150 @Override 151 public final org.jdom2.Element addContent(String text) { 152 // ignore empty elements 153 if (text == null) { 154 return null; 155 } 156 return super.addContent(cleanUpString(text)); 157 } 158 159 /** 160 * Appends all children in the given collection to the end of the content list. In event of an exception 161 * during add the original content will be unchanged and the objects in the supplied collection will be unaltered. 162 * @param collection Collection to append. Null values are ignored. 163 * @return the element on which the method was called 164 */ 165 @Override 166 public final org.jdom2.Element addContent(Collection collection) { 167 // ignore empty elements 168 if (collection == null) { 169 return null; 170 } 171 return super.addContent(collection); 172 } 173 174 /** 175 * Inserts the content in a collection into the content list at the given index. In event of an exception 176 * the original content will be unchanged and the objects in the supplied collection will be unaltered. 177 * @param index Location for adding the collection 178 * @param collection Collection to insert. Null values are ignored. 179 * @return The parent on which the method was called 180 */ 181 @Override 182 public final org.jdom2.Element addContent(int index, Collection collection) { 183 // ignore empty elements 184 if (collection == null) { 185 return null; 186 } 187 return super.addContent(index, collection); 188 } 189 190 /** 191 * Appends the child to the end of the element's content list. 192 * Returns not the element itself (contrary to addContent), but a reference to the newly added element. 193 * @param <T> Type that extends Element 194 * @param element Element to add. Null values are ignored. 195 * @return The added element. 196 */ 197 public final <T extends AbstractElement> T add(T element) { 198 this.addContent(element); 199 return element; 200 } 201 202 /** 203 * Sets the content of the element to be the text given. All existing text content and non-text context is removed. 204 * If this element should have both textual content and nested elements, use <code>{@link #setContent}</code> instead. 205 * Setting a null text value is equivalent to setting an empty string value. 206 * @param text new text content for the element 207 * @return the target element 208 * @throws org.jdom2.IllegalDataException if the assigned text contains an illegal character such as a 209 * vertical tab (as determined by {@link org.jdom2.Verifier#checkCharacterData}) 210 */ 211 @Override 212 public org.jdom2.Element setText(String text) { 213 return super.setText(cleanUpString(StringUtils.defaultString(text))); 214 } 215 216 /** 217 * This sets an attribute value for this element. Any existing attribute with the same name and namespace URI is 218 * removed. 219 * @param name name of the attribute to set 220 * @param value value of the attribute to set 221 * @param ns namespace of the attribute to set 222 * @return this element modified 223 * @throws org.jdom2.IllegalNameException if the given name is illegal as an attribute name, or if the namespace 224 * is an unprefixed default namespace 225 * @throws org.jdom2.IllegalDataException if the given attribute value is illegal character data (as determined 226 * by {@link org.jdom2.Verifier#checkCharacterData}). 227 * @throws org.jdom2.IllegalAddException if the attribute namespace prefix collides with another namespace 228 * prefix on the element. 229 */ 230 @Override 231 public final org.jdom2.Element setAttribute(String name, String value, Namespace ns) { 232 // remove attribute if value is set to null 233 if (value == null) { 234 super.removeAttribute(name, ns); 235 return this; 236 } 237 else { 238 return super.setAttribute(name, cleanUpString(value), ns); 239 } 240 } 241 242 /** 243 * This sets an attribute value for this element. Any existing attribute with the same name and namespace URI is 244 * removed. 245 * @param name name of the attribute to set 246 * @param value value of the attribute to set 247 * @return this element modified 248 * @throws org.jdom2.IllegalNameException if the given name is illegal as an attribute name. 249 * @throws org.jdom2.IllegalDataException if the given attribute value is illegal character data 250 * (as determined by {@link org.jdom2.Verifier#checkCharacterData}). 251 */ 252 @Override 253 public final org.jdom2.Element setAttribute(String name, String value) { 254 // remove attribute if value is set to null 255 if (value == null) { 256 super.removeAttribute(name); 257 return this; 258 } 259 else { 260 return super.setAttribute(name, cleanUpString(value)); 261 } 262 } 263 264 /** 265 * Cleans up the given string and removes all control char characters not valid for XML. 266 * These control chars e.g. 0x03 for UTF-8 can by pasted by copy&paste into the CMS RTE. 267 * @param text Text 268 * @return Cleaned up text 269 */ 270 private String cleanUpString(String text) { 271 if (StringUtils.isEmpty(text)) { 272 return text; 273 } 274 else { 275 return CONTROL_CHARS.matcher(text).replaceAll(""); 276 } 277 } 278 279 @Override 280 public final String toString() { 281 return new XMLOutputter().outputString(this); 282 } 283 284 /** 285 * @return Content of element serialized as string 286 */ 287 public final String toStringContentOnly() { 288 return new XMLOutputter().outputElementContentString(this); 289 } 290 291 }