package org.dcache.boot; import java.io.PrintStream; import java.util.Enumeration; import org.dcache.util.ConfigurationProperties; /** * Print the configuration as a series of statements that define XML * entities; for each property there is a line like: * <p> * <tt><!ENTITY entityName "entityValue"></tt> * <p> * The set of valid entity names is a proper subset of the * valid property names. Because of this, some property names are * mapped to the nearest valid entity name by substituting invalid * characters to the underscore character. In practice, this * shouldn't matter as current naming convention with property names * makes this a non-issue. * <p> * The entityValue is based on the propertyValue and includes all * necessary XML markup. * <p> * The output from this printer may be saved in a file and included * in any XML file using the following DTD at the beginning of the * file: * <pre> * <?xml version="1.0" encoding="UTF-8" ?> * <!DOCTYPE top-element [ * <!ENTITY % properties-data "/path/to/file.xml"> * %properties-data; * ]> * </pre> * where <tt>top-element</tt> is the name of the top-most element in * the XML file and <tt>/path/to/file.xml</tt> is the path to the * file containing this generated data. With this, parameter values * may be referenced using <tt>&entityName;</tt> inside the * XML document. */ public class XmlEntityLayoutPrinter implements LayoutPrinter { private final Layout _layout; private final String[] _prefixes; /** * Create a new XmlEntityLayoutPrinter taking properties from * the global context of the supplied layout file. * <p> * Zero or more prefixes may be provided. If any prefixes are supplied then * a property's key must start with at least one prefix for it to be included * in the output. As a special case, omitting any prefixes will result in * all properties being included in the output. */ public XmlEntityLayoutPrinter(Layout layout, String... prefixes) { _layout = layout; _prefixes = prefixes; } @Override public void print(PrintStream out) { ConfigurationProperties properties = _layout.properties(); Enumeration<?> propertyNames = properties.propertyNames(); while( propertyNames.hasMoreElements()) { String key = (String) propertyNames.nextElement(); if(isToBePrinted(key)) { String value = properties.getValue(key); out.println("<!ENTITY " + entityNameFrom(key) + " \"" + entityValueFrom(value) + "\">"); } } } private boolean isToBePrinted(String key) { boolean printEntity = _prefixes.length == 0; for(String prefix : _prefixes) { if(key.startsWith(prefix)) { printEntity = true; break; } } return printEntity; } private static String entityNameFrom(String key) { StringBuilder sb = new StringBuilder(); char first = key.charAt(0); sb.append( isValidEntityNameStartCharacter(first) ? first : "_"); for( int i = 1; i < key.length(); i++) { char c = key.charAt(i); sb.append( isValidEntityNameCharacter(c) ? c : "_"); } return sb.toString(); } private static boolean isValidEntityNameStartCharacter( char c) { if( hasCharType(c, Character.LOWERCASE_LETTER, Character.UPPERCASE_LETTER)) { return true; } return c == '_'; } private static boolean isValidEntityNameCharacter( char c) { if( hasCharType(c, Character.LOWERCASE_LETTER, Character.UPPERCASE_LETTER, Character.DECIMAL_DIGIT_NUMBER)) { return true; } return c == '-' || c == '.'; } private static boolean hasCharType(char c, int... types) { int charType = Character.getType(c); for (int type : types) { if (charType == type) { return true; } } return false; } private static String entityValueFrom( String s) { return s.replace("&","&").replace("\"", """).replace("'", "'").replace("<", "<").replace("%", "%"); } }