001: /* $Id: SetPropertiesRule.java 467222 2006-10-24 03:17:11Z markt $
002: *
003: * Licensed to the Apache Software Foundation (ASF) under one or more
004: * contributor license agreements. See the NOTICE file distributed with
005: * this work for additional information regarding copyright ownership.
006: * The ASF licenses this file to You under the Apache License, Version 2.0
007: * (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: package org.apache.tomcat.util.digester;
020:
021: import org.apache.tomcat.util.IntrospectionUtils;
022: import org.xml.sax.Attributes;
023:
024: /**
025: * <p>Rule implementation that sets properties on the object at the top of the
026: * stack, based on attributes with corresponding names.</p>
027: *
028: * <p>This rule supports custom mapping of attribute names to property names.
029: * The default mapping for particular attributes can be overridden by using
030: * {@link #SetPropertiesRule(String[] attributeNames, String[] propertyNames)}.
031: * This allows attributes to be mapped to properties with different names.
032: * Certain attributes can also be marked to be ignored.</p>
033: */
034:
035: public class SetPropertiesRule extends Rule {
036:
037: // ----------------------------------------------------------- Constructors
038:
039: /**
040: * Default constructor sets only the the associated Digester.
041: *
042: * @param digester The digester with which this rule is associated
043: *
044: * @deprecated The digester instance is now set in the {@link Digester#addRule} method.
045: * Use {@link #SetPropertiesRule()} instead.
046: */
047: public SetPropertiesRule(Digester digester) {
048:
049: this ();
050:
051: }
052:
053: /**
054: * Base constructor.
055: */
056: public SetPropertiesRule() {
057:
058: // nothing to set up
059:
060: }
061:
062: /**
063: * <p>Convenience constructor overrides the mapping for just one property.</p>
064: *
065: * <p>For details about how this works, see
066: * {@link #SetPropertiesRule(String[] attributeNames, String[] propertyNames)}.</p>
067: *
068: * @param attributeName map this attribute
069: * @param propertyName to a property with this name
070: */
071: public SetPropertiesRule(String attributeName, String propertyName) {
072:
073: attributeNames = new String[1];
074: attributeNames[0] = attributeName;
075: propertyNames = new String[1];
076: propertyNames[0] = propertyName;
077: }
078:
079: /**
080: * <p>Constructor allows attribute->property mapping to be overriden.</p>
081: *
082: * <p>Two arrays are passed in.
083: * One contains the attribute names and the other the property names.
084: * The attribute name / property name pairs are match by position
085: * In order words, the first string in the attribute name list matches
086: * to the first string in the property name list and so on.</p>
087: *
088: * <p>If a property name is null or the attribute name has no matching
089: * property name, then this indicates that the attibute should be ignored.</p>
090: *
091: * <h5>Example One</h5>
092: * <p> The following constructs a rule that maps the <code>alt-city</code>
093: * attribute to the <code>city</code> property and the <code>alt-state</code>
094: * to the <code>state</code> property.
095: * All other attributes are mapped as usual using exact name matching.
096: * <code><pre>
097: * SetPropertiesRule(
098: * new String[] {"alt-city", "alt-state"},
099: * new String[] {"city", "state"});
100: * </pre></code>
101: *
102: * <h5>Example Two</h5>
103: * <p> The following constructs a rule that maps the <code>class</code>
104: * attribute to the <code>className</code> property.
105: * The attribute <code>ignore-me</code> is not mapped.
106: * All other attributes are mapped as usual using exact name matching.
107: * <code><pre>
108: * SetPropertiesRule(
109: * new String[] {"class", "ignore-me"},
110: * new String[] {"className"});
111: * </pre></code>
112: *
113: * @param attributeNames names of attributes to map
114: * @param propertyNames names of properties mapped to
115: */
116: public SetPropertiesRule(String[] attributeNames,
117: String[] propertyNames) {
118: // create local copies
119: this .attributeNames = new String[attributeNames.length];
120: for (int i = 0, size = attributeNames.length; i < size; i++) {
121: this .attributeNames[i] = attributeNames[i];
122: }
123:
124: this .propertyNames = new String[propertyNames.length];
125: for (int i = 0, size = propertyNames.length; i < size; i++) {
126: this .propertyNames[i] = propertyNames[i];
127: }
128: }
129:
130: // ----------------------------------------------------- Instance Variables
131:
132: /**
133: * Attribute names used to override natural attribute->property mapping
134: */
135: private String[] attributeNames;
136: /**
137: * Property names used to override natural attribute->property mapping
138: */
139: private String[] propertyNames;
140:
141: // --------------------------------------------------------- Public Methods
142:
143: /**
144: * Process the beginning of this element.
145: *
146: * @param attributes The attribute list of this element
147: */
148: public void begin(Attributes attributes) throws Exception {
149:
150: // Populate the corresponding properties of the top object
151: Object top = digester.peek();
152: if (digester.log.isDebugEnabled()) {
153: if (top != null) {
154: digester.log.debug("[SetPropertiesRule]{"
155: + digester.match + "} Set "
156: + top.getClass().getName() + " properties");
157: } else {
158: digester.log.debug("[SetPropertiesRule]{"
159: + digester.match + "} Set NULL properties");
160: }
161: }
162:
163: // set up variables for custom names mappings
164: int attNamesLength = 0;
165: if (attributeNames != null) {
166: attNamesLength = attributeNames.length;
167: }
168: int propNamesLength = 0;
169: if (propertyNames != null) {
170: propNamesLength = propertyNames.length;
171: }
172:
173: for (int i = 0; i < attributes.getLength(); i++) {
174: String name = attributes.getLocalName(i);
175: if ("".equals(name)) {
176: name = attributes.getQName(i);
177: }
178: String value = attributes.getValue(i);
179:
180: // we'll now check for custom mappings
181: for (int n = 0; n < attNamesLength; n++) {
182: if (name.equals(attributeNames[n])) {
183: if (n < propNamesLength) {
184: // set this to value from list
185: name = propertyNames[n];
186:
187: } else {
188: // set name to null
189: // we'll check for this later
190: name = null;
191: }
192: break;
193: }
194: }
195:
196: if (digester.log.isDebugEnabled()) {
197: digester.log.debug("[SetPropertiesRule]{"
198: + digester.match + "} Setting property '"
199: + name + "' to '" + value + "'");
200: }
201: IntrospectionUtils.setProperty(top, name, value);
202: }
203:
204: }
205:
206: /**
207: * <p>Add an additional attribute name to property name mapping.
208: * This is intended to be used from the xml rules.
209: */
210: public void addAlias(String attributeName, String propertyName) {
211:
212: // this is a bit tricky.
213: // we'll need to resize the array.
214: // probably should be synchronized but digester's not thread safe anyway
215: if (attributeNames == null) {
216:
217: attributeNames = new String[1];
218: attributeNames[0] = attributeName;
219: propertyNames = new String[1];
220: propertyNames[0] = propertyName;
221:
222: } else {
223: int length = attributeNames.length;
224: String[] tempAttributes = new String[length + 1];
225: for (int i = 0; i < length; i++) {
226: tempAttributes[i] = attributeNames[i];
227: }
228: tempAttributes[length] = attributeName;
229:
230: String[] tempProperties = new String[length + 1];
231: for (int i = 0; i < length && i < propertyNames.length; i++) {
232: tempProperties[i] = propertyNames[i];
233: }
234: tempProperties[length] = propertyName;
235:
236: propertyNames = tempProperties;
237: attributeNames = tempAttributes;
238: }
239: }
240:
241: /**
242: * Render a printable version of this Rule.
243: */
244: public String toString() {
245:
246: StringBuffer sb = new StringBuffer("SetPropertiesRule[");
247: sb.append("]");
248: return (sb.toString());
249:
250: }
251:
252: }
|