/* * 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.cocoon.components.modules.input; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.component.ComponentSelector; import org.apache.avalon.framework.thread.ThreadSafe; import java.util.Map; import java.util.List; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; /** * This modules allows to "chain" several other modules. If a module * returns "null" as attribute value, the next module in the chain is * queried until either a value can be obtained or the end of the * chain is reached. * * <p>A typical example would be to "chain" request parameters, * session attributes, and constants in this order. This way, an * application could have a default skin that could be overridden by a * user in her/his profile stored in the session. In addition, the * user could request a different skin through passing a request * parameter.</p> * * <p>Usage:</p> * * <p> Any number of <input-module/> blocks may appear in the * component configuration. The @name attribute is used as the name of * the requested input module. The complete <input-module/> * block is passed at run-time to the module and thus can contain any * configuration data for that particular module.</p> * * <p>Configuration:</p> * * <p>It can be controlled whether it returns a flat or a deep view, * i.e. whether only values from the first module are returned if * non-null or they are merged with values from other modules * <code><all-values>true</all-values></code>. The same is * possible for the attribute names * (<code><all-names/></code>). In addition, empty strings could * be treated the same as null values * (<code><empty-as-null/></code>).</p> * * @author <a href="mailto:haul@apache.org">Christian Haul</a> * @version CVS $Id$ */ public class ChainMetaModule extends AbstractMetaModule implements ThreadSafe { private ModuleHolder[] inputs = null; private boolean emptyAsNull = false; private boolean allNames = false; private boolean allValues = false; public void configure(Configuration config) throws ConfigurationException { Configuration[] confs = config.getChildren("input-module"); if (confs.length > 0) { this.inputs = new ModuleHolder[confs.length]; int j = 0; for (int i=0; i<confs.length; i++) { ModuleHolder module = new ModuleHolder(); module.name = confs[i].getAttribute("name",null); if (module.name == null) { if (getLogger().isErrorEnabled()) getLogger().error("No name attribute for module configuration. Skipping."); continue; } module.config = confs[i]; this.inputs[j]=module; j++; } } this.emptyAsNull = config.getChild("empty-as-null").getValueAsBoolean(this.emptyAsNull); this.allNames = config.getChild("all-names").getValueAsBoolean(this.allNames); this.allValues = config.getChild("all-values").getValueAsBoolean(this.allValues); } public synchronized void lazy_initialize() { try { // obtain input modules if (!this.initialized) { this.inputSelector=(ComponentSelector) this.manager.lookup(INPUT_MODULE_SELECTOR); if (this.inputSelector != null && this.inputSelector instanceof ThreadSafe) { for (int i=0; i<this.inputs.length; i++) { if (this.inputs[i].name != null) this.inputs[i].input = obtainModule(this.inputs[i].name); } } else if (!(this.inputSelector instanceof ThreadSafe) ) { this.manager.release(this.inputSelector); this.inputSelector = null; } this.initialized = true; } } catch (Exception e) { if (getLogger().isWarnEnabled()) getLogger().warn("A problem occurred setting up input modules :'" + e.getMessage()); } } public void dispose() { if (this.inputSelector != null) { for (int i=0; i<this.inputs.length; i++) { if (this.inputs[i].input != null) this.inputSelector.release(this.inputs[i].input); } this.manager.release(this.inputSelector); } } public Object[] getAttributeValues( String attr, Configuration modeConf, Map objectModel ) throws ConfigurationException { if (!this.initialized) { this.lazy_initialize(); } // obtain correct configuration objects // default vs dynamic Configuration[] inputConfigs = null; boolean allValues = this.allValues; boolean emptyAsNull = this.emptyAsNull; if (modeConf!=null && modeConf.getChildren().length > 0) { inputConfigs = modeConf.getChildren("input-module"); emptyAsNull = modeConf.getChild("empty-as-null").getValueAsBoolean(emptyAsNull); allValues = modeConf.getChild("all-values").getValueAsBoolean(allValues); if (inputConfigs.length == 0) inputConfigs = null; } Object[] value = null; boolean debug = getLogger().isDebugEnabled(); List values = null; if (allValues) values = new ArrayList(); if (inputConfigs == null) { // static configuration branch int i = 0; while (i < this.inputs.length && (value == null || allValues)) { if (this.inputs[i].name != null) { value = getValues(attr, objectModel, this.inputs[i].input, this.inputs[i].name, this.inputs[i].config); if (emptyAsNull && value != null && value.length == 0) value = null; if (emptyAsNull && value != null && value.length == 1 && value[0] instanceof String && value[0].equals("")) value = null; if (debug) getLogger().debug("read from "+this.inputs[i].name+" attribute "+attr+" as "+value); if (allValues && value != null) values.addAll(Arrays.asList(value)); } i++; } } else { // run-time configuration branch int i = 0; while (i < inputConfigs.length && (value == null || allValues)) { String name = inputConfigs[i].getAttribute("name",null); if (name != null) { value = getValues(attr, objectModel, null, name, inputConfigs[i]); if (emptyAsNull && value != null && value.length == 0) value = null; if (emptyAsNull && value != null && value.length == 1 && value[0] instanceof String && value[0].equals("")) value = null; if (debug) getLogger().debug("read from "+name+" attribute "+attr+" as "+value); if (allValues && value != null) values.addAll(Arrays.asList(value)); } i++; } } if (debug) getLogger().debug("result chaining for "+attr+" is "+(allValues? values.toArray() : value)); return (allValues? values.toArray() : value); } private void addIterator(Collection col, Iterator iter) { while (iter != null && iter.hasNext()) col.add(iter.next()); } public Iterator getAttributeNames( Configuration modeConf, Map objectModel ) throws ConfigurationException { if (!this.initialized) { this.lazy_initialize(); } // obtain correct configuration objects // default vs dynamic Configuration[] inputConfigs = null; boolean emptyAsNull = this.emptyAsNull; boolean allNames = this.allNames; if (modeConf!=null && modeConf.getChildren().length > 0) { inputConfigs = modeConf.getChildren("input-module"); emptyAsNull = modeConf.getChild("empty-as-null").getValueAsBoolean(emptyAsNull); allNames = modeConf.getChild("all-names").getValueAsBoolean(allNames); if (inputConfigs.length == 0) inputConfigs = null; } Iterator value = null; Collection values = null; if (allNames) values = new ArrayList(); boolean debug = getLogger().isDebugEnabled(); if (inputConfigs == null) { // static configuration branch int i = 0; while (i < this.inputs.length && (value == null || allNames)) { if (this.inputs[i].name != null) { value = getNames(objectModel, this.inputs[i].input, this.inputs[i].name, this.inputs[i].config); if (debug) getLogger().debug("read from "+this.inputs[i].name+" AttributeNames as "+value); if (allNames && value != null) addIterator(values, value); } i++; } } else { // run-time configuration branch int i = 0; while (i < inputConfigs.length && value == null) { String name = inputConfigs[i].getAttribute("name",null); if (name != null) { value = getNames(objectModel, null, name, inputConfigs[i]); if (debug) getLogger().debug("read from "+name+" AttributeNames as "+value); if (allNames && value != null) addIterator(values, value); } i++; } } if (debug) getLogger().debug("result chaining names is "+(allNames? values.iterator() : value)); return (allNames? values.iterator() : value); } public Object getAttribute( String attr, Configuration modeConf, Map objectModel ) throws ConfigurationException { Object[] values = this.getAttributeValues(attr,modeConf,objectModel); if (getLogger().isDebugEnabled()) getLogger().debug("result chaining single for "+attr+" is "+(values != null? values[0] : "null")); return (values != null? values[0] : null); } }