/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.catalina.storeconfig; import java.beans.IndexedPropertyDescriptor; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.io.PrintWriter; import java.net.InetAddress; import java.util.Iterator; import org.apache.tomcat.util.IntrospectionUtils; import org.apache.tomcat.util.descriptor.web.ResourceBase; /** * StoreAppends generate really the xml tag elements */ public class StoreAppender { /** * The set of classes that represent persistable properties. */ private static Class<?> persistables[] = { String.class, Integer.class, Integer.TYPE, Boolean.class, Boolean.TYPE, Byte.class, Byte.TYPE, Character.class, Character.TYPE, Double.class, Double.TYPE, Float.class, Float.TYPE, Long.class, Long.TYPE, Short.class, Short.TYPE, InetAddress.class }; private int pos = 0; /** * Print the closing tag. * * @param aWriter The output writer * @param aDesc Store description of the current element * @throws Exception A store error occurred */ public void printCloseTag(PrintWriter aWriter, StoreDescription aDesc) throws Exception { aWriter.print("</"); aWriter.print(aDesc.getTag()); aWriter.println(">"); } /** * Print only the open tag with all attributes. * * @param aWriter The output writer * @param indent Indentation level * @param bean The current bean that is stored * @param aDesc Store description of the current element * @throws Exception A store error occurred */ public void printOpenTag(PrintWriter aWriter, int indent, Object bean, StoreDescription aDesc) throws Exception { aWriter.print("<"); aWriter.print(aDesc.getTag()); if (aDesc.isAttributes() && bean != null) printAttributes(aWriter, indent, bean, aDesc); aWriter.println(">"); } /** * Print tag with all attributes * * @param aWriter The output writer * @param indent Indentation level * @param bean The current bean that is stored * @param aDesc Store description of the current element * @throws Exception A store error occurred */ public void printTag(PrintWriter aWriter, int indent, Object bean, StoreDescription aDesc) throws Exception { aWriter.print("<"); aWriter.print(aDesc.getTag()); if (aDesc.isAttributes() && bean != null) printAttributes(aWriter, indent, bean, aDesc); aWriter.println("/>"); } /** * Print the value from tag as content. * * @param aWriter The output writer * @param tag The element name * @param content Element content * @throws Exception A store error occurred */ public void printTagContent(PrintWriter aWriter, String tag, String content) throws Exception { aWriter.print("<"); aWriter.print(tag); aWriter.print(">"); aWriter.print(convertStr(content)); aWriter.print("</"); aWriter.print(tag); aWriter.println(">"); } /** * Print an array of values. * * @param aWriter The output writer * @param tag The element name * @param indent Indentation level * @param elements Array of element values */ public void printTagValueArray(PrintWriter aWriter, String tag, int indent, String[] elements) { if (elements != null && elements.length > 0) { printIndent(aWriter, indent + 2); aWriter.print("<"); aWriter.print(tag); aWriter.print(">"); for (int i = 0; i < elements.length; i++) { printIndent(aWriter, indent + 4); aWriter.print(elements[i]); if (i + 1 < elements.length) aWriter.println(","); } printIndent(aWriter, indent + 2); aWriter.print("</"); aWriter.print(tag); aWriter.println(">"); } } /** * Print an array of elements. * * @param aWriter The output writer * @param tag The element name * @param indent Indentation level * @param elements Array of elements * @throws Exception Store error occurred */ public void printTagArray(PrintWriter aWriter, String tag, int indent, String[] elements) throws Exception { if (elements != null) { for (int i = 0; i < elements.length; i++) { printIndent(aWriter, indent); printTagContent(aWriter, tag, elements[i]); } } } /** * Print some spaces. * * @param aWriter The output writer * @param indent The number of spaces */ public void printIndent(PrintWriter aWriter, int indent) { for (int i = 0; i < indent; i++) { aWriter.print(' '); } pos = indent; } /** * Store the relevant attributes of the specified JavaBean, plus a * <code>className</code> attribute defining the fully qualified Java * class name of the bean. * * @param writer PrintWriter to which we are storing * @param indent Indentation level * @param bean * Bean whose properties are to be rendered as attributes, * @param desc Store description of the current element * * @exception Exception * if an exception occurs while storing */ public void printAttributes(PrintWriter writer, int indent, Object bean, StoreDescription desc) throws Exception { printAttributes(writer, indent, true, bean, desc); } /** * Store the relevant attributes of the specified JavaBean. * * @param writer PrintWriter to which we are storing * @param indent Indentation level * @param include * Should we include a <code>className</code> attribute? * @param bean * Bean whose properties are to be rendered as attributes, * @param desc * RegistryDescriptor from this bean * * @exception Exception * if an exception occurs while storing */ public void printAttributes(PrintWriter writer, int indent, boolean include, Object bean, StoreDescription desc) throws Exception { // Render a className attribute if requested if (include && desc != null && !desc.isStandard()) { writer.print(" className=\""); writer.print(bean.getClass().getName()); writer.print("\""); } // Acquire the list of properties for this bean PropertyDescriptor descriptors[] = Introspector.getBeanInfo( bean.getClass()).getPropertyDescriptors(); if (descriptors == null) { descriptors = new PropertyDescriptor[0]; } // Create blank instance Object bean2 = defaultInstance(bean); for (int i = 0; i < descriptors.length; i++) { if (descriptors[i] instanceof IndexedPropertyDescriptor) { continue; // Indexed properties are not persisted } if (!isPersistable(descriptors[i].getPropertyType()) || (descriptors[i].getReadMethod() == null) || (descriptors[i].getWriteMethod() == null)) { continue; // Must be a read-write primitive or String } if (desc.isTransientAttribute(descriptors[i].getName())) { continue; // Skip the specified exceptions } Object value = IntrospectionUtils.getProperty(bean, descriptors[i] .getName()); if (value == null) { continue; // Null values are not persisted } Object value2 = IntrospectionUtils.getProperty(bean2, descriptors[i].getName()); if (value.equals(value2)) { // The property has its default value continue; } printAttribute(writer, indent, bean, desc, descriptors[i].getName(), bean2, value); } if (bean instanceof ResourceBase) { ResourceBase resource = (ResourceBase) bean; for (Iterator<String> iter = resource.listProperties(); iter.hasNext();) { String name = iter.next(); Object value = resource.getProperty(name); if (!isPersistable(value.getClass())) { continue; } if (desc.isTransientAttribute(name)) { continue; // Skip the specified exceptions } printValue(writer, indent, name, value); } } } /** * Store the specified of the specified JavaBean. * * @param writer PrintWriter to which we are storing * @param indent Indentation level * @param bean The current bean * @param desc RegistryDescriptor from this bean * @param attributeName The attribute name to store * @param bean2 A default instance of the bean for comparison * @param value The attribute value */ protected void printAttribute(PrintWriter writer, int indent, Object bean, StoreDescription desc, String attributeName, Object bean2, Object value) { if (isPrintValue(bean, bean2, attributeName, desc)) printValue(writer, indent, attributeName, value); } /** * Determine if the attribute value needs to be stored. * * @param bean * orginal bean * @param bean2 * default bean * @param attrName * attribute name * @param desc * StoreDescription from bean * @return <code>true</code> if the value should be stored */ public boolean isPrintValue(Object bean, Object bean2, String attrName, StoreDescription desc) { boolean printValue = false; Object value = IntrospectionUtils.getProperty(bean, attrName); if (value != null) { Object value2 = IntrospectionUtils.getProperty(bean2, attrName); printValue = !value.equals(value2); } return printValue; } /** * Generate default Instance for the specified bean. * * @param bean The bean * @return an object from same class as bean parameter * @throws InstantiationException Error creating a new instance * @throws IllegalAccessException Another error occurred */ public Object defaultInstance(Object bean) throws InstantiationException, IllegalAccessException { return bean.getClass().newInstance(); } /** * Print an attribute value. * * @param writer PrintWriter to which we are storing * @param indent Indentation level * @param name Attribute name * @param value Attribute value */ public void printValue(PrintWriter writer, int indent, String name, Object value) { // Convert IP addresses to strings so they will be persisted if (value instanceof InetAddress) { value = ((InetAddress) value).getHostAddress(); } if (!(value instanceof String)) { value = value.toString(); } String strValue = convertStr((String) value); pos = pos + name.length() + strValue.length(); if (pos > 60) { writer.println(); printIndent(writer, indent + 4); } else { writer.print(' '); } writer.print(name); writer.print("=\""); writer.print(strValue); writer.print("\""); } /** * Given a string, this method replaces all occurrences of '<', '>', * '&', and '"'. * @param input The string to escape * @return the escaped string */ public String convertStr(String input) { StringBuffer filtered = new StringBuffer(input.length()); char c; for (int i = 0; i < input.length(); i++) { c = input.charAt(i); if (c == '<') { filtered.append("<"); } else if (c == '>') { filtered.append(">"); } else if (c == '\'') { filtered.append("'"); } else if (c == '"') { filtered.append("""); } else if (c == '&') { filtered.append("&"); } else { filtered.append(c); } } return filtered.toString(); } /** * Is the specified property type one for which we should generate a * persistence attribute? * * @param clazz * Java class to be tested * @return <code>true</code> if the specified class should be stored */ protected boolean isPersistable(Class<?> clazz) { for (int i = 0; i < persistables.length; i++) { if (persistables[i] == clazz || persistables[i].isAssignableFrom(clazz)) { return true; } } return false; } }