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

com.feilong.lib.digester3.SetPropertiesRule Maven / Gradle / Ivy

Go to download

feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.

There is a newer version: 4.0.8
Show newest version
package com.feilong.lib.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 java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;

import com.feilong.lib.beanutils.BeanUtils;
import com.feilong.lib.beanutils.PropertyUtils;

/**
 * 

* 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{ /** The Constant log. */ private static final Logger LOGGER = LoggerFactory.getLogger(SetPropertiesRule.class); // ----------------------------------------------------------- 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 (LOGGER.isDebugEnabled()){ LOGGER.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 = PropertyUtils.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 (LOGGER.isDebugEnabled()){ if (top != null){ LOGGER.debug(format("[SetPropertiesRule]{%s} Set '%s' properties", getDigester().getMatch(), top.getClass().getName())); }else{ LOGGER.debug(format("[SetPropertiesRule]{%s} Set NULL properties", getDigester().getMatch())); } } BeanUtils.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