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

org.apache.commons.digester3.SetPropertiesRule Maven / Gradle / Ivy

Go to download

The Apache Commons Digester package lets you configure an XML to Java object mapping module which triggers certain actions called rules whenever a particular pattern of nested XML elements is recognized.

The newest version!
package org.apache.commons.digester3;

/*
 * 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.
 */

import static java.lang.String.format;
import static org.apache.commons.beanutils.BeanUtils.populate;
import static org.apache.commons.beanutils.PropertyUtils.isWriteable;

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

import org.xml.sax.Attributes;

/**
 * 

* Rule implementation that sets properties on the object at the top of the stack, based on attributes with * corresponding names. *

*

* This rule supports custom mapping of attribute names to property names. The default mapping for particular attributes * can be overridden by using {@link #SetPropertiesRule(String[] attributeNames, String[] propertyNames)}. This allows * attributes to be mapped to properties with different names. Certain attributes can also be marked to be ignored. *

*/ public class SetPropertiesRule extends Rule { // ----------------------------------------------------------- Constructors /** * Base constructor. */ public SetPropertiesRule() { // nothing to set up } /** *

* Convenience constructor overrides the mapping for just one property. *

*

* For details about how this works, see {@link #SetPropertiesRule(String[] attributeNames, String[] propertyNames)} * . *

* * @param attributeName map this attribute * @param propertyName to a property with this name */ public SetPropertiesRule( String attributeName, String propertyName ) { aliases.put( attributeName, propertyName ); } /** *

* Constructor allows attribute->property mapping to be overriden. *

*

* Two arrays are passed in. One contains the attribute names and the other the property names. The attribute name / * property name pairs are match by position In order words, the first string in the attribute name list matches to * the first string in the property name list and so on. *

*

* If a property name is null or the attribute name has no matching property name, then this indicates that the * attibute should be ignored. *

*
Example One
*

* The following constructs a rule that maps the alt-city attribute to the city property * and the alt-state to the state property. All other attributes are mapped as usual using * exact name matching.

     *      SetPropertiesRule(
     *                new String[] {"alt-city", "alt-state"},
     *                new String[] {"city", "state"});
     * 
*
Example Two
*

* The following constructs a rule that maps the class attribute to the className * property. The attribute ignore-me is not mapped. All other attributes are mapped as usual using * exact name matching.

     *      SetPropertiesRule(
     *                new String[] {"class", "ignore-me"},
     *                new String[] {"className"});
     * 
* * @param attributeNames names of attributes to map * @param propertyNames names of properties mapped to */ public SetPropertiesRule( String[] attributeNames, String[] propertyNames ) { for ( int i = 0, size = attributeNames.length; i < size; i++ ) { String propName = null; if ( i < propertyNames.length ) { propName = propertyNames[i]; } aliases.put( attributeNames[i], propName ); } } /** * Constructor allows attribute->property mapping to be overriden. * * @param aliases attribute->property mapping * @since 3.0 */ public SetPropertiesRule( Map aliases ) { if ( aliases != null && !aliases.isEmpty() ) { this.aliases.putAll( aliases ); } } // ----------------------------------------------------- Instance Variables private final Map aliases = new HashMap(); /** * Used to determine whether the parsing should fail if an property specified in the XML is missing from the bean. * Default is true for backward compatibility. */ private boolean ignoreMissingProperty = true; // --------------------------------------------------------- Public Methods /** * {@inheritDoc} */ @Override public void begin( String namespace, String name, Attributes attributes ) throws Exception { // Build a set of attribute names and corresponding values Map values = new HashMap(); for ( int i = 0; i < attributes.getLength(); i++ ) { String attributeName = attributes.getLocalName( i ); if ( "".equals( attributeName ) ) { attributeName = attributes.getQName( i ); } String value = attributes.getValue( i ); // alias lookup has complexity O(1) if ( aliases.containsKey( attributeName ) ) { attributeName = aliases.get( attributeName ); } if ( getDigester().getLogger().isDebugEnabled() ) { getDigester().getLogger().debug( format( "[SetPropertiesRule]{%s} Setting property '%s' to '%s'", getDigester().getMatch(), attributeName, attributeName ) ); } if ( ( !ignoreMissingProperty ) && ( attributeName != null ) ) { // The BeanUtils.populate method silently ignores items in // the map (ie xml entities) which have no corresponding // setter method, so here we check whether each xml attribute // does have a corresponding property before calling the // BeanUtils.populate method. // // Yes having the test and set as separate steps is ugly and // inefficient. But BeanUtils.populate doesn't provide the // functionality we need here, and changing the algorithm which // determines the appropriate setter method to invoke is // considered too risky. // // Using two different classes (PropertyUtils vs BeanUtils) to // do the test and the set is also ugly; the codepaths // are different which could potentially lead to trouble. // However the BeanUtils/ProperyUtils code has been carefully // compared and the PropertyUtils functionality does appear // compatible so we'll accept the risk here. Object top = getDigester().peek(); boolean test = isWriteable( top, attributeName ); if ( !test ) { throw new NoSuchMethodException( "Property " + attributeName + " can't be set" ); } } if ( attributeName != null ) { values.put( attributeName, value ); } } // Populate the corresponding properties of the top object Object top = getDigester().peek(); if ( getDigester().getLogger().isDebugEnabled() ) { if ( top != null ) { getDigester().getLogger().debug( format( "[SetPropertiesRule]{%s} Set '%s' properties", getDigester().getMatch(), top.getClass().getName() ) ); } else { getDigester().getLogger().debug( format( "[SetPropertiesRule]{%s} Set NULL properties", getDigester().getMatch() ) ); } } populate( top, values ); } /** * Add an additional attribute name to property name mapping. This is intended to be used from the xml rules. * * @param attributeName the attribute name has to be mapped * @param propertyName the target property name */ public void addAlias( String attributeName, String propertyName ) { aliases.put( attributeName, propertyName ); } /** * {@inheritDoc} */ @Override public String toString() { return format( "SetPropertiesRule[aliases=%s, ignoreMissingProperty=%s]", aliases, ignoreMissingProperty ); } /** *

* Are attributes found in the xml without matching properties to be ignored? *

*

* If false, the parsing will interrupt with an NoSuchMethodException if a property specified in the * XML is not found. The default is true. *

* * @return true if skipping the unmatched attributes. */ public boolean isIgnoreMissingProperty() { return this.ignoreMissingProperty; } /** * Sets whether attributes found in the xml without matching properties should be ignored. If set to false, the * parsing will throw an NoSuchMethodException if an unmatched attribute is found. This allows to trap * misspellings in the XML file. * * @param ignoreMissingProperty false to stop the parsing on unmatched attributes. */ public void setIgnoreMissingProperty( boolean ignoreMissingProperty ) { this.ignoreMissingProperty = ignoreMissingProperty; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy