All Downloads are FREE. Search and download functionalities are using the official Maven repository.

es.rickyepoderi.wbxml.definition.WbXmlInitialization Maven / Gradle / Ivy

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *    
 * Linking this library statically or dynamically with other modules 
 * is making a combined work based on this library. Thus, the terms and
 * conditions of the GNU General Public License cover the whole
 * combination.
 *    
 * As a special exception, the copyright holders of this library give 
 * you permission to link this library with independent modules to 
 * produce an executable, regardless of the license terms of these 
 * independent modules, and to copy and distribute the resulting 
 * executable under terms of your choice, provided that you also meet, 
 * for each linked independent module, the terms and conditions of the 
 * license of that module.  An independent module is a module which 
 * is not derived from or based on this library.  If you modify this 
 * library, you may extend this exception to your version of the 
 * library, but you are not obligated to do so.  If you do not wish 
 * to do so, delete this exception statement from your version.
 *
 * Project: github.com/rickyepoderi/wbxml-stream
 * 
 */
package es.rickyepoderi.wbxml.definition;

import es.rickyepoderi.wbxml.document.OpaqueAttributePlugin;
import es.rickyepoderi.wbxml.document.OpaqueContentPlugin;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

/**
 * 

The initialization class is a way to load into the JVM all the language * definitions that the implementation is going to use. The class is maybe * a bit overelaborated.

* *

The class loads all the definitions that are paced in a specified * classpath location:

* *

es/rickyepoderi/wbxml/definition/defaults

* *

This location can be overriden using a system property:

* *

-Des.rickyepoderi.wbxml.definition.path=new/classpath/resource/path

* *

Remember that the languages are loaded from the classpath, not from a * directory in the file system. Only JAR or normal directory can be used * to place the property files.

* *

All the definition properties file should be of the name * wbxml.<LANG_NAME>.properties * and they are just loaded into the system. Then all the languages can be * retrieved using the getDefinition* methods of this class.

* * @author ricky */ public class WbXmlInitialization { /** * Logger for the class. */ protected static final Logger log = Logger.getLogger(WbXmlInitialization.class.getName()); // // language location and name syntax // /** * The system property name for changing default classpath location * of the language definition properties file, */ static public final String RESOURCE_DIRECTORY_PROPERTY = "es.rickyepoderi.wbxml.definition.path"; /** * Default location for language definitions. */ static public final String DEFAULT_RESOURCE_DIRECTORY = "es/rickyepoderi/wbxml/definition/defaults"; /** * Suffix for the properties file of definitions. */ static public final String PROPERTIES_SUFFIX = ".properties"; /** * Prefix for the properties file odf definitions. */ static public final String PROPERTIES_PREFIX = "wbxml."; // // Name for keys inside the property file // /** * Name for the property that has the language name. It is compulsory. */ static public final String PROP_WBXML_NAME = "wbxml.name"; /** * Name for the property that has the publid id of the definition. It should * be 0x01 (unknown) if the definitions has no standard name. */ static public final String PROP_WBXML_PUBLIC_ID = "wbxml.publicid"; /** * Name for the property that has the FPI of the definition. It can * be omitted if the definition has no formal id but it that case the * recognition of the language can be impossible and parsing/decoding * needs direct specification of the definition language to use. */ static public final String PROP_WBXML_XML_PUBLIC_IDENTIFIER = "wbxml.xmlpublicidentifier"; /** * Name for the property that has the URI for dtd files of the definition. * It can be null. */ static public final String PROP_WBXML_XML_URI_REFERENCE = "wbxml.xmlurireference"; /** * Name for the property that specifies the root element of the language. * Please set it with some value (in case the definition has several possible * root element just put the most common one). This should be prefixed if * namespaces are used. */ static public final String PROP_WBXML_ROOT_ELEMENT = "wbxml.rootelement"; /** * The name for the property that specifies the JAXB class for the root element. * This can be null. In the implementation no JAXB classes are packaged * inside the library. In the test part there are some of them just for testing * purposes. */ static public final String PROP_WBXML_CLASS_ELEMENT = "wbxml.class"; /** * Prefix for properties that handle the language tags. The tags are in the * following form: * *
     * wbxml.tag.{pageCode}.[{prefix}:]{name}={token}
     * 
*/ static public final String PROP_WBXML_TAG_PREFIX = "wbxml.tag."; /** * Prefix for properties that defines the namespaces used in the * definition. There are a lot of languages that do not use them and in * case they are used, all the tags should be prefixed. The * format is the following: * *
     * wbxml.namespaces.{prefix}={namespaceURI}
     * 
*/ static public final String PROP_WBXML_NAMESPACE_PREFIX = "wbxml.namespaces."; /** * Prefix for properties that handle the language attributes. The attributes * can define part of the value so the format is a bit different and it is * based in two properties. The first one is used to specify the page and * the numeric token and the second one (optional) if some value is used. * *
     * wbxml.attr.{pageCode}.[{prefix}:]{name}[.{optional-differenciator}]={token}
     * {previous-key}.value={optional-value}
     * 
*/ static public final String PROP_WBXML_ATTR_PREFIX = "wbxml.attr."; /** * Prefix for properties that handle attribute values. The values are * common values for attributes that has a specified token in order * to save space in the binary WBXML representation. The format uses again * two properties: * *
     * wbxml.attrvalue.{pageCode}[.{optional}]={token}
     * {previous_key}.value={value}
     * 
*/ static public final String PROP_WBXML_ATTR_VALUE_PREFIX = "wbxml.attrvalue."; /** * Prefix for properties that handle extensions in the language. Extensions * are similar to attribute values but it can be used in any string value * (attribute of content). Again the format consists in two properties: * *
     * wbxml.ext.{key_differenciator}={token}
     * {previous_key}.value={value}
     * 
*/ static public final String PROP_WBXML_EXT_PREFIX = "wbxml.ext."; /** * Prefix used for opaques for attribute values. The opaque is a weird * way of encoding any special value in a wbxml file. In this case the * opaque will pase/encode special attribute values. */ static public final String PROP_WBXML_OPAQUE_ATTR_PREFIX = "wbxml.opaque.attr."; /** * Prefix used for opaques that are used for tags. The opaque is a weird * way of encoding any special value in a wbxml file. In this case the * opaque will parse/encode tag contents. * *
     * webxml.opaque.attr.{pageCode}.{name}={class}
     * 
*/ static public final String PROP_WBXML_OPAQUE_TAG_PREFIX = "wbxml.opaque.tag."; /** * The suffix use when a second property is needed (attributes, extensions * and so on). */ static public final String PROP_WBXML_VALUE_SUFFIX = ".value"; /** * Prefix used for properties that has the linked definitions. Linked * definitions are not standard and just are used for weird languages * which mix another ones (SyncML for instance). The format is the * following: * *
     * wbxml.opaque.linkeddef.{differenciator}={language_name}
     * 
*/ static public final String PROP_WBXML_LINKED_DEF = "wbxml.opaque.linkeddef."; /** * Static list of definitions that are loaded at initialization of this class. */ static private List definitions = null; // // Private method to load and parse the files // /** * Private method that constructs the tag using the property. * @param key The property key for tag * @param value The value (the token) * @return The WbXxmlTagDef that this property represents */ static private WbXmlTagDef getTagDefinition(String key, String value) { try { // the line is "wbxml.tag..[:]=" String[] keys = key.split(Pattern.quote(".")); byte pageCode = Integer.decode(keys[2]).byteValue(); String name = keys[3]; String prefix = null; int idx = name.indexOf(':'); if (idx > 0) { prefix = name.substring(0, idx); name = name.substring(idx + 1); } byte token = Integer.decode(value).byteValue(); return new WbXmlTagDef(prefix, name, token, pageCode); } catch (Exception e) { log.log(Level.SEVERE, "Error loading attribute {0}={1}", new Object[]{key, value}); log.log(Level.SEVERE, "Exception", e); return null; } } /** * Private method that constructs the namespace using the property. * @param key The property key for the namespace * @param value The value for the namespace (the namespaceURI) * @return The WbXmlNamespaceDef that this property represents */ static private WbXmlNamespaceDef getNamespaceDefinition(String key, String value) { try { // the line is "wbxml.namespaces.=" String[] keys = key.split(Pattern.quote(".")); String prefix = keys[2]; String namespace = value; return new WbXmlNamespaceDef(prefix, namespace); } catch (Exception e) { log.log(Level.SEVERE, "Error loading attribute {0}={1}", new Object[]{key, value}); log.log(Level.SEVERE, "Exception", e); return null; } } /** * Private method that constructs the attribute using the property. As * attributes use two properties the second property will be found inside * the properties again. * @param props The properties to search for the second key * @param key The property key for the attribute * @param value The value of the key (the token) * @return The WbXmlAttributeDef that this property represents */ static private WbXmlAttributeDef getAttrDefinition(Properties props, String key, String value) { try { // two possible lines // compulsory line: wbxml.attr..[:][.]= // optional line : .value= String val = props.getProperty(key + PROP_WBXML_VALUE_SUFFIX); String[] keys = key.split(Pattern.quote(".")); byte pageCode = Integer.decode(keys[2]).byteValue(); String name = keys[3]; String prefix = null; int idx = name.indexOf(':'); if (idx > 0) { prefix = name.substring(0, idx); name = name.substring(idx + 1); } byte token = Integer.decode(value).byteValue(); return new WbXmlAttributeDef(prefix, name, token, pageCode, val); } catch (Exception e) { log.log(Level.SEVERE, "Error loading attribute {0}={1}", new Object[]{key, value}); log.log(Level.SEVERE, "Exception", e); return null; } } /** * Private method that constructs the attribute value using the property. As * attribute values use two properties the second property will be found inside * the properties again. * @param props The properties to search for the second key * @param The property key for the attribute value * @param value The value of the key (the token) * @return The WbXmlAttributeValueDef that this property represents */ static private WbXmlAttributeValueDef getAttrValueDefinition(Properties props, String key, String value) { try { // two possible lines // compulsory line: wbxml.attrvalue.[.]= // optional line : .value= String[] keys = key.split(Pattern.quote(".")); byte pageCode = Integer.decode(keys[2]).byteValue(); byte token = Integer.decode(value).byteValue(); String val = props.getProperty(key + PROP_WBXML_VALUE_SUFFIX); return new WbXmlAttributeValueDef(val, token, pageCode); } catch (Exception e) { log.log(Level.SEVERE, "Error loading attribute value {0}={1}", new Object[]{key, value}); log.log(Level.SEVERE, "Exception", e); return null; } } /** * Private method that constructs the extension using the property. As * extensions use two properties the second property will be found inside * the properties again. * @param props The properties to search for the second key * @param The property key for the extension * @param value The value of the key (the token) * @return The WbXmlExtensionDef that this property represents */ static private WbXmlExtensionDef getExtDefinition(Properties props, String key, String value) { try { // two possible lines // compulsory line: wbxml.ext.= // optional line : .value= byte token = Integer.decode(value).byteValue(); String val = props.getProperty(key + PROP_WBXML_VALUE_SUFFIX); return new WbXmlExtensionDef(val, token); } catch (Exception e) { log.log(Level.SEVERE, "Error loading extension {0}={1}", new Object[] {key, value}); log.log(Level.SEVERE, "Exception", e); return null; } } /** * Private method that add a new opaque for attributes to the definition. * @param def The definition to add the opaque to * @param props The properties to search the attribute the opaque is used to * @param key The property key for the opaque attr * @param value The value (class) of the property */ static private void addOpaqueAttrPlugin(WbXmlDefinition def, Properties props, String key, String value) { try { // the line is: webxml.opaque.attr..= String[] keys = key.split(Pattern.quote(".")); byte pageCode = Integer.decode(keys[3]).byteValue(); String name = keys[4]; String tagProp = new StringBuilder(PROP_WBXML_ATTR_PREFIX) .append(pageCode) .append(".") .append(name).toString(); WbXmlAttributeDef attrDef = getAttrDefinition(props, tagProp, props.getProperty(tagProp)); Class clazz = Class.forName(value); OpaqueAttributePlugin plugin = (OpaqueAttributePlugin) clazz.newInstance(); def.addOpaqueAttr(attrDef, plugin); } catch (Exception e) { log.log(Level.SEVERE, "Error loading plugin {0}={1}", new Object[] {key, value}); log.log(Level.SEVERE, "Exception", e); } } /** * Private method that add a new opaque for tags/content to the definition. * @param def The definition to add the opaque to * @param props The properties to search the tag the opaque is used to * @param key The property key for the opaque tag * @param value The value (class) of the property */ static private void addOpaqueTagPlugin(WbXmlDefinition def, Properties props, String key, String value) { try { // the line is: webxml.opaque.tag..= String[] keys = key.split(Pattern.quote(".")); byte pageCode = Integer.decode(keys[3]).byteValue(); String name = keys[4]; String tagProp = new StringBuilder(PROP_WBXML_TAG_PREFIX) .append(pageCode) .append(".") .append(name).toString(); WbXmlTagDef tagDef = getTagDefinition(tagProp, props.getProperty(tagProp)); Class clazz = Class.forName(value); OpaqueContentPlugin plugin = (OpaqueContentPlugin) clazz.newInstance(); def.addOpaqueTag(tagDef, plugin); } catch (Exception e) { log.log(Level.SEVERE, "Error loading plugin {0}={1}", new Object[] {key, value}); log.log(Level.SEVERE, "Exception", e); } } /** * Private method that adds the linked definition to the current one. This * method is calling during the file processing, cos the linked definition * could not be loaded yet it just store the name. Then another method * will put the complete definition. * @param def The definition which is being processed * @param value The name of the linked definition */ static private void addLinkedDefinition(WbXmlDefinition def, String value) { // just add the definition name // at the end the real definiion will be added def.getLinkedDefinitions().put(value, null); } /** * Private method that processes a properties file and load a complete * definition. Only the linked definitions need a post processing loop. * @param props The properties of the definition file * @return The WbXmlDefinition loaded (linked part is not completely done) */ static private WbXmlDefinition loadDefinition(Properties props) { // read the main parameters for the definition String name = props.getProperty(PROP_WBXML_NAME); long publicId = Long.decode(props.getProperty(PROP_WBXML_PUBLIC_ID)); String xmlPublicId = props.getProperty(PROP_WBXML_XML_PUBLIC_IDENTIFIER); String xmlUriRef = props.getProperty(PROP_WBXML_XML_URI_REFERENCE); String clazz = props.getProperty(PROP_WBXML_CLASS_ELEMENT); WbXmlDefinition def = new WbXmlDefinition(name, publicId, xmlPublicId, xmlUriRef, clazz); // read all the tags for (String key: props.stringPropertyNames()) { if (key.startsWith(PROP_WBXML_TAG_PREFIX)) { WbXmlTagDef tag = getTagDefinition(key, props.getProperty(key)); if (tag != null) { def.addTag(tag); } } else if (key.startsWith(PROP_WBXML_NAMESPACE_PREFIX)) { WbXmlNamespaceDef ns = getNamespaceDefinition(key, props.getProperty(key)); if (ns != null) { def.addNamespace(ns); } } else if (key.startsWith(PROP_WBXML_ATTR_PREFIX) && !key.endsWith(PROP_WBXML_VALUE_SUFFIX)) { WbXmlAttributeDef attr = getAttrDefinition(props, key, props.getProperty(key)); if (attr != null) { def.addAttr(attr); } } else if (key.startsWith(PROP_WBXML_ATTR_VALUE_PREFIX) && !key.endsWith(PROP_WBXML_VALUE_SUFFIX)) { WbXmlAttributeValueDef attrVal = getAttrValueDefinition(props, key, props.getProperty(key)); if (attrVal != null) { def.addAttrValue(attrVal); } } else if (key.startsWith(PROP_WBXML_EXT_PREFIX) && !key.endsWith(PROP_WBXML_VALUE_SUFFIX)) { WbXmlExtensionDef ext = getExtDefinition(props, key, props.getProperty(key)); if (ext != null) { def.addExtension(ext); } } else if (key.startsWith(PROP_WBXML_OPAQUE_ATTR_PREFIX)) { addOpaqueAttrPlugin(def, props, key, props.getProperty(key)); } else if (key.startsWith(PROP_WBXML_OPAQUE_TAG_PREFIX)) { addOpaqueTagPlugin(def, props, key, props.getProperty(key)); } else if (key.startsWith(PROP_WBXML_LINKED_DEF)) { addLinkedDefinition(def, props.getProperty(key)); } } String root = props.getProperty(PROP_WBXML_ROOT_ELEMENT); def.setRoot(root); return def; } /** * Private method that load the properties file from a JAR file. The JAR * is iterated searching for the definition files and the definitions * are loaded. * @param resource The url resource that contains the JAR * @param path The path defined to contain the definitions */ static private void loadPropertiesJar(URL resource, String path) { JarURLConnection jarConn; JarFile jar = null; try { jarConn = (JarURLConnection) resource.openConnection(); jar = jarConn.getJarFile(); Enumeration e = jar.entries(); while (e.hasMoreElements()) { JarEntry entry = e.nextElement(); if (entry.getName().startsWith(path + "/" + PROPERTIES_PREFIX) && entry.getName().endsWith(PROPERTIES_SUFFIX)) { Properties props = new Properties(); props.load(jar.getInputStream(entry)); definitions.add(loadDefinition(props)); } } } catch (IOException e) { log.log(Level.SEVERE, "loadPropertiesJar(): Error loading definition {0}", resource); log.log(Level.SEVERE, "loadPropertiesJar(): Error loading definition...", e); } finally { try { if (jar != null) { jar.close(); } } catch (IOException e) { } } } /** * Private method that loads the definitions using normal directory * container. * @param file The directory that contains the definitions */ static private void loadPropertiesFile(File file) { if (file.isDirectory()) { File[] files = file.listFiles(); for (File f : files) { if (f.isFile() && f.getName().endsWith(PROPERTIES_SUFFIX) && f.getName().startsWith(PROPERTIES_PREFIX)) { FileReader reader = null; try { Properties props = new Properties(); reader = new FileReader(f); props.load(reader); definitions.add(loadDefinition(props)); } catch (IOException e) { log.log(Level.SEVERE, "loadPropertiesFile(): Error loading definition {0}", file.getAbsoluteFile()); log.log(Level.SEVERE, "loadPropertiesFile(): Error loading definition...", e); } finally { if (reader != null) { try { reader.close(); } catch (IOException ex) { } } } } } } } /** * Private method that does the post-processing part for definitions. * This method iterates all the definitions and, in case linked definitions * are defined, the real definition is placed in there. */ static private void processLinkedDefinitions() { for (WbXmlDefinition def: getDefinitions()) { if (!def.getLinkedDefinitions().isEmpty()) { // iterate over the definitions and add the real one for (String name: def.getLinkedDefinitions().keySet()) { WbXmlDefinition linkedDef = getDefinitionByName(name); if (linkedDef != null) { // add the real linked def def.getLinkedDefinitions().put(name, linkedDef); } else { // the definition is not correctly defined => warn it def.getLinkedDefinitions().remove(name); log.log(Level.WARNING, "The linked definition {0} defined in {1} does not exists", new Object[]{name, def.getName()}); } } } } } /** * Private method that does the initialization and loading of all the * definition languages. The property files can be placed in the classpath * (using a JAR file or a common directory). */ static private void init() { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); String resourcePath = System.getProperty(RESOURCE_DIRECTORY_PROPERTY); if (resourcePath == null) { resourcePath = DEFAULT_RESOURCE_DIRECTORY; } URL resource = classLoader.getResource(resourcePath); if (resource == null) { log.log(Level.SEVERE, "The definition path {0} is not found inside the classpath", resource); throw new IllegalStateException( String.format("The definition path '%s' is not found inside the classpath", resourcePath)); } if (resource.getProtocol().equals("jar")) { loadPropertiesJar(resource, DEFAULT_RESOURCE_DIRECTORY); processLinkedDefinitions(); } else if (resource.getProtocol().equals("file")) { loadPropertiesFile(new File(resource.getFile())); processLinkedDefinitions(); } else { log.log(Level.SEVERE, "Invalid protocol for the definitions directory: {0}", resource); throw new IllegalStateException( String.format("Invalid protocol for the definitions directory '%s'", resource.getProtocol())); } } // // Public methods to get the definitions // /** * Method that returns all the definitions in the system. * @return The list of definitions */ static synchronized public WbXmlDefinition[] getDefinitions() { return definitions.toArray(new WbXmlDefinition[0]); } /** * Method that return the definition using the FPI of the language. * @param fpi The FPI of the language * @return The definition for that language or null */ static synchronized public WbXmlDefinition getDefinitionByFPI(String fpi) { for (WbXmlDefinition def: definitions) { if (def.getXmlPublicId() != null && def.getXmlPublicId().equals(fpi)) { return def; } } return null; } /** * Method that return the definition using the public id of the language. * @param id The public id of the language * @return The definition for that language or null */ static synchronized public WbXmlDefinition getDefinitionByPublicId(long id) { for (WbXmlDefinition def: definitions) { if (def.getPublicId() == id) { return def; } } return null; } /** * Method that return the definition using the name of the language. * @param name The name of the language * @return The definition for that language or null */ static synchronized public WbXmlDefinition getDefinitionByName(String name) { for (WbXmlDefinition def: definitions) { if (def.getName() != null && def.getName().equals(name)) { return def; } } return null; } /** * Method that return the definition using the root element of the language. * @param root The root name * @param namespaceUri The namespace of the root element * @return The definition for that language or null */ static synchronized public WbXmlDefinition getDefinitionByRoot(String root, String namespaceUri) { for (WbXmlDefinition def : definitions) { if (namespaceUri != null && !namespaceUri.isEmpty()) { // get the prefix for this namespace String prefix = def.getPrefix(namespaceUri); if (prefix != null) { root = new StringBuilder(prefix).append(":").append(root).toString(); } } if (def.getRoot() != null && def.getRoot().getNameWithPrefix().equals(root)) { return def; } } return null; } /** * Method that reloads the definitions using normal processing. */ static synchronized public void reload() { definitions.clear(); init(); } // // Initialization // static { definitions = new ArrayList(); init(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy