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

org.apache.struts2.views.xslt.AdapterFactory Maven / Gradle / Ivy

There is a newer version: 6.4.0
Show newest version
/*
 * 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.struts2.views.xslt;

import org.apache.struts2.StrutsException;
import org.w3c.dom.*;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * 

* AdapterFactory produces Node adapters for Java object types. * Adapter classes are generally instantiated dynamically via a no-args constructor * and populated with their context information via the AdapterNode interface. *

* *

* This factory supports proxying of generic DOM Node trees, allowing arbitrary * Node types to be mixed together. You may simply return a Document or Node * type as an object property and it will appear as a sub-tree in the XML as * you'd expect. See #proxyNode(). *

* *

* Customization of the result XML can be accomplished by providing * alternate adapters for Java types. Adapters are associated with Java * types through the registerAdapterType() method. *

* *

* For example, since there is no default Date adapter, Date objects will be * rendered with the generic Bean introspecting adapter, producing output * like: *

* *
 *     <date>
 *      <date>19</date>
 *      <day>1</day>
 *      <hours>0</hours>
 *      <minutes>7</minutes>
 *      <month>8</month>
 *      <seconds>4</seconds>
 *      <time>1127106424531</time>
 *      <timezoneOffset>300</timezoneOffset>
 *      <year>105</year>
 *     </date>
 * 
* *

* By extending the StringAdapter and overriding its normal behavior we can * create a custom Date formatter: *

* *
 *     public static class CustomDateAdapter extends StringAdapter {
 *       protected String getStringValue() {
 *           Date date = (Date)getPropertyValue();
 *           return DateFormat.getTimeInstance( DateFormat.FULL ).format( date );
 *       }
 *   }
 * 
* *

* Producing output like: *

* *
 *    <date>12:02:54 AM CDT</date>
 * 
* *

* The StringAdapter (which is normally invoked only to adapt String values) * is a useful base for these kinds of customizations and can produce * structured XML output as well as plain text by setting its parseStringAsXML() * property to true. *

* *

* See provided examples. *

*/ public class AdapterFactory { private Map adapterTypes = new HashMap<>(); /** * Register an adapter type for a Java class type. * * @param type the Java class type which is to be handled by the adapter. * @param adapterType The adapter class, which implements AdapterNode. */ public void registerAdapterType(Class type, Class adapterType) { adapterTypes.put(type, adapterType); } /** * Create a top level Document adapter for the specified Java object. * The document will have a root element with the specified property name * and contain the specified Java object content. * * @param propertyName the name of the root document element * @param propertyValue the property value * * @return the document object * * @throws IllegalAccessException in case of illegal access * @throws InstantiationException in case of instantiation errors */ public Document adaptDocument(String propertyName, Object propertyValue) throws IllegalAccessException, InstantiationException { return new SimpleAdapterDocument(this, null, propertyName, propertyValue); } /** * Create an Node adapter for a child element. * Note that the parent of the created node must be an AdapterNode, however * the child node itself may be any type of Node. * * @see #adaptDocument( String, Object ) * * @param parent the parent adapter node * @param propertyName the name of th property * @param value the value * * @return a node */ public Node adaptNode(AdapterNode parent, String propertyName, Object value) { Class adapterClass = getAdapterForValue(value); if (adapterClass != null) { return constructAdapterInstance(adapterClass, parent, propertyName, value); } // If the property is a Document, "unwrap" it to the root element if (value instanceof Document) { value = ((Document) value).getDocumentElement(); } // If the property is already a Node, proxy it if (value instanceof Node) { return proxyNode(parent, (Node) value); } // Check other supported types or default to generic JavaBean introspecting adapter Class valueType = value.getClass(); if (valueType.isArray()) { adapterClass = ArrayAdapter.class; } else if (value instanceof String || value instanceof Number || value instanceof Boolean || valueType.isPrimitive()) { adapterClass = StringAdapter.class; } else if (value instanceof Collection) { adapterClass = CollectionAdapter.class; } else if (value instanceof Map) { adapterClass = MapAdapter.class; } else { adapterClass = BeanAdapter.class; } return constructAdapterInstance(adapterClass, parent, propertyName, value); } /** *

* Construct a proxy adapter for a value that is an existing DOM Node. * This allows arbitrary DOM Node trees to be mixed in with our results. * The proxied nodes are read-only and currently support only * limited types of Nodes including Element, Text, and Attributes. (Other * Node types may be ignored by the proxy and not appear in the result tree). *

* *

* // TODO: * NameSpaces are not yet supported. *

* *

* This method is primarily for use by the adapter node classes. *

* * @param parent parent adapter node * @param node node * * @return proxy node */ public Node proxyNode(AdapterNode parent, Node node) { // If the property is a Document, "unwrap" it to the root element if (node instanceof Document) { node = ((Document) node).getDocumentElement(); } if (node == null) { return null; } if (node.getNodeType() == Node.ELEMENT_NODE) { return new ProxyElementAdapter(this, parent, (Element) node); } if (node.getNodeType() == Node.TEXT_NODE) { return new ProxyTextNodeAdapter(this, parent, (Text) node); } if (node.getNodeType() == Node.ATTRIBUTE_NODE) { return new ProxyAttrAdapter(this, parent, (Attr) node); } return null; // Unsupported Node type - ignore for now } public NamedNodeMap proxyNamedNodeMap(AdapterNode parent, NamedNodeMap nnm) { return new ProxyNamedNodeMap(this, parent, nnm); } /** * Create an instance of an adapter dynamically and set its context via * the AdapterNode interface. * * @param adapterClass adapter class * @param parent parent adapter node * @param propertyName the property name * @param propertyValue the property value * * @return the new node */ private Node constructAdapterInstance(Class adapterClass, AdapterNode parent, String propertyName, Object propertyValue) { // Check to see if the class has a no-args constructor try { adapterClass.getConstructor(new Class []{}); } catch (NoSuchMethodException e1) { throw new StrutsException("Adapter class: " + adapterClass + " does not have a no-args constructor."); } try { AdapterNode adapterNode = (AdapterNode) adapterClass.newInstance(); adapterNode.setAdapterFactory(this); adapterNode.setParent(parent); adapterNode.setPropertyName(propertyName); adapterNode.setPropertyValue(propertyValue); return adapterNode; } catch (IllegalAccessException | InstantiationException e) { throw new StrutsException("Cannot adapt " + propertyValue + " (" + propertyName + ") :" + e.getMessage(), e); } } /** * Create an appropriate adapter for a null value. * * @param parent parent adapter node * @param propertyName the property name * * @return the new node */ public Node adaptNullValue(AdapterNode parent, String propertyName) { return new StringAdapter(this, parent, propertyName, "null"); } //TODO: implement Configuration option to provide additional adapter classes public Class getAdapterForValue(Object value) { return adapterTypes.get(value.getClass()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy