Java tutorial
/* * 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.treeprocessor.sitemap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.ServletContext; import org.apache.avalon.excalibur.pool.Recyclable; import org.apache.avalon.framework.activity.Disposable; import org.apache.avalon.framework.activity.Initializable; import org.apache.avalon.framework.configuration.AbstractConfiguration; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.configuration.SAXConfigurationHandler; import org.apache.avalon.framework.context.Context; import org.apache.avalon.framework.context.ContextException; import org.apache.avalon.framework.context.Contextualizable; import org.apache.avalon.framework.service.ServiceException; import org.apache.avalon.framework.service.ServiceManager; import org.apache.avalon.framework.service.Serviceable; import org.apache.commons.lang.StringUtils; import org.apache.excalibur.source.Source; import org.apache.excalibur.source.SourceResolver; import org.apache.regexp.RE; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.web.context.WebApplicationContext; import org.apache.cocoon.Constants; import org.apache.cocoon.classloader.reloading.Monitor; import org.apache.cocoon.components.LifecycleHelper; import org.apache.cocoon.components.pipeline.impl.PipelineComponentInfo; import org.apache.cocoon.components.source.util.SourceUtil; import org.apache.cocoon.components.treeprocessor.AbstractProcessingNode; import org.apache.cocoon.components.treeprocessor.CategoryNode; import org.apache.cocoon.components.treeprocessor.CategoryNodeBuilder; import org.apache.cocoon.components.treeprocessor.ConcreteTreeProcessor; import org.apache.cocoon.components.treeprocessor.LinkedProcessingNodeBuilder; import org.apache.cocoon.components.treeprocessor.NodeBuilderSelector; import org.apache.cocoon.components.treeprocessor.ParameterizableProcessingNode; import org.apache.cocoon.components.treeprocessor.ProcessingNode; import org.apache.cocoon.components.treeprocessor.ProcessingNodeBuilder; import org.apache.cocoon.components.treeprocessor.TreeBuilder; import org.apache.cocoon.components.treeprocessor.variables.VariableResolver; import org.apache.cocoon.components.treeprocessor.variables.VariableResolverFactory; import org.apache.cocoon.configuration.Settings; import org.apache.cocoon.core.container.spring.avalon.AvalonUtils; import org.apache.cocoon.core.container.spring.avalon.SitemapHelper; import org.apache.cocoon.generation.Generator; import org.apache.cocoon.serialization.Serializer; import org.apache.cocoon.sitemap.EnterSitemapEventListener; import org.apache.cocoon.sitemap.LeaveSitemapEventListener; import org.apache.cocoon.sitemap.PatternException; import org.apache.cocoon.sitemap.SitemapParameters; import org.apache.cocoon.util.AbstractLogEnabled; import org.apache.cocoon.util.location.Location; import org.apache.cocoon.util.location.LocationImpl; import org.apache.cocoon.util.location.LocationUtils; /** * The tree builder for the sitemap language. * * @version $Id: SitemapLanguage.java 587751 2007-10-24 02:41:36Z vgritsenko $ */ public class SitemapLanguage extends AbstractLogEnabled implements TreeBuilder, Contextualizable, Serviceable, Recyclable { // Regexp's for splitting expressions private static final String COMMA_SPLIT_REGEXP = "[\\s]*,[\\s]*"; private static final String EQUALS_SPLIT_REGEXP = "[\\s]*=[\\s]*"; protected Map attributes = new HashMap(); // ----- lifecycle-related objects ------ /** * This component's avalon context */ private Context context; /** * This component's service manager */ private ServiceManager manager; // ------------------------------------- /** * The tree processor that we are building. */ protected ConcreteTreeProcessor processor; /** * The namespace of configuration for the processor that we are building. */ protected String itsNamespace; /** * The service manager for the processor that we are building. */ private ServiceManager itsManager; private WebApplicationContext itsContainer; /** * Helper object which sets up components in the context of the processor * that we are building. */ private LifecycleHelper itsLifecycle; /** * Selector for ProcessingNodeBuilders which is set up in the context of the * processor that we are building. */ private NodeBuilderSelector itsBuilders; /** * The sitemap component information grabbed while building itsMaanger */ protected PipelineComponentInfo itsComponentInfo; /** Optional event listeners for the enter sitemap event */ protected List enterSitemapEventListeners = new ArrayList(); /** Optional event listeners for the leave sitemap event */ protected List leaveSitemapEventListeners = new ArrayList(); // ------------------------------------- /** Nodes gone through setupNode() that implement Initializable */ private List initializableNodes = new ArrayList(); /** Nodes gone through setupNode() that implement Disposable */ private List disposableNodes = new ArrayList(); /** * NodeBuilders created by createNodeBuilder() that implement * LinkedProcessingNodeBuilder */ private List linkedBuilders = new ArrayList(); /** Are we in a state that allows to get registered nodes ? */ private boolean canGetNode = false; /** Nodes registered using registerNode() */ private Map registeredNodes = new HashMap(); /** * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context) */ public void contextualize(Context avalonContext) throws ContextException { this.context = avalonContext; } /** * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager) */ public void service(ServiceManager serviceManager) throws ServiceException { this.manager = serviceManager; } /** * Get the location of the treebuilder config file. Can be overridden for * other versions. * * @return The location of the treebuilder config file */ protected String getBuilderConfigURL() { return "resource://org/apache/cocoon/components/treeprocessor/sitemap-language.xml"; } /** * @see org.apache.cocoon.components.treeprocessor.TreeBuilder#setAttribute(java.lang.String, * java.lang.Object) */ public void setAttribute(String name, Object value) { this.attributes.put(name, value); } /** * @see org.apache.cocoon.components.treeprocessor.TreeBuilder#getAttribute(java.lang.String) */ public Object getAttribute(String name) { return this.attributes.get(name); } /** * @see org.apache.cocoon.components.treeprocessor.TreeBuilder#setProcessor(ConcreteTreeProcessor) */ public void setProcessor(ConcreteTreeProcessor processor) { this.processor = processor; } /** * @see org.apache.cocoon.components.treeprocessor.TreeBuilder#getProcessor() */ public ConcreteTreeProcessor getProcessor() { return this.processor; } /** * @see org.apache.cocoon.components.treeprocessor.TreeBuilder#getWebApplicationContext() */ public WebApplicationContext getWebApplicationContext() { return this.itsContainer; } /** * @see org.apache.cocoon.components.treeprocessor.TreeBuilder#getEnterSitemapEventListeners() */ public List getEnterSitemapEventListeners() { // we make a copy here, so we can clear(recylce) the list after the // sitemap is build return (List) ((ArrayList) this.enterSitemapEventListeners).clone(); } /** * @see org.apache.cocoon.components.treeprocessor.TreeBuilder#getLeaveSitemapEventListeners() */ public List getLeaveSitemapEventListeners() { // we make a copy here, so we can clear(recylce) the list after the // sitemap is build return (List) ((ArrayList) this.leaveSitemapEventListeners).clone(); } /** * @see org.apache.cocoon.components.treeprocessor.TreeBuilder#registerNode(java.lang.String, * org.apache.cocoon.components.treeprocessor.ProcessingNode) */ public boolean registerNode(String name, ProcessingNode node) { if (this.registeredNodes.containsKey(name)) { return false; } this.registeredNodes.put(name, node); return true; } /** * @see org.apache.cocoon.components.treeprocessor.TreeBuilder#getRegisteredNode(java.lang.String) */ public ProcessingNode getRegisteredNode(String name) { if (this.canGetNode) { return (ProcessingNode) this.registeredNodes.get(name); } throw new IllegalArgumentException("Categories are only available during buildNode()"); } /** * @see org.apache.cocoon.components.treeprocessor.TreeBuilder#createNodeBuilder(org.apache.avalon.framework.configuration.Configuration) */ public ProcessingNodeBuilder createNodeBuilder(Configuration config) throws Exception { // FIXME : check namespace String nodeName = config.getName(); if (getLogger().isDebugEnabled()) { getLogger().debug("Creating node builder for " + nodeName); } ProcessingNodeBuilder builder; builder = (ProcessingNodeBuilder) this.itsBuilders.getBuilder(nodeName); builder.setBuilder(this); if (builder instanceof LinkedProcessingNodeBuilder) { this.linkedBuilders.add(builder); } return builder; } /** * Create the tree once component manager and node builders have been set * up. Can be overriden by subclasses to perform pre/post tree creation * operations. */ protected ProcessingNode createTree(Configuration tree) throws Exception { // Create a node builder from the top-level element ProcessingNodeBuilder rootBuilder = createNodeBuilder(tree); // Build the whole tree (with an empty buildModel) return rootBuilder.buildNode(tree); } /** * Resolve links : call <code>linkNode()</code> on all * <code>LinkedProcessingNodeBuilder</code>s. Can be overriden by * subclasses to perform pre/post resolution operations. * * Before linking nodes, lookup the view category node used in * {@link #getViewNodes(Collection)}. */ protected void linkNodes() throws Exception { // Get the views category node this.viewsNode = CategoryNodeBuilder.getCategoryNode(this, "views"); // Resolve links Iterator iter = this.linkedBuilders.iterator(); while (iter.hasNext()) { ((LinkedProcessingNodeBuilder) iter.next()).linkNode(); } } /** * Get the namespace URI that builders should use to find their nodes. */ public String getNamespace() { return this.itsNamespace; } /** * Build a processing tree from a <code>Configuration</code>. */ public ProcessingNode build(Configuration tree, String location) throws Exception { // TODO rcl // final Monitor fam = (Monitor) this.manager.lookup(Monitor.ROLE); // fam.setSitemapNotifier(this.processor.getWrappingProcessor()); final Monitor fam = null; this.itsContainer = SitemapHelper.createContainer(tree, location, fam, (ServletContext) this.context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT)); final Context itsContext = (Context) this.itsContainer.getBean(AvalonUtils.CONTEXT_ROLE); // The namespace used in the whole sitemap is the one of the root // element this.itsNamespace = tree.getNamespace(); // replace properties? if (tree.getChild("components").getAttributeAsBoolean("replace-properties", true)) { tree = AvalonUtils.replaceProperties(tree, (Settings) this.itsContainer.getBean(Settings.ROLE)); } this.itsManager = (ServiceManager) this.itsContainer.getBean(AvalonUtils.SERVICE_MANAGER_ROLE); // register listeners this.registerListeners(); this.itsComponentInfo = (PipelineComponentInfo) this.itsManager.lookup(PipelineComponentInfo.ROLE); // Create a helper object to setup components this.itsLifecycle = new LifecycleHelper(null /* logger */, itsContext, this.itsManager, null /* configuration */); // Create & initialize the NodeBuilder selector. { NodeBuilderSelector selector = new NodeBuilderSelector(); // Load the builder config file SourceResolver resolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE); String url = getBuilderConfigURL(); Configuration config; try { Source src = resolver.resolveURI(url); try { SAXConfigurationHandler handler = new SAXConfigurationHandler(); SourceUtil.toSAX(this.manager, src, null, handler); config = handler.getConfiguration(); } finally { resolver.release(src); } } catch (Exception e) { throw new ConfigurationException("Could not load TreeBuilder configuration from " + url, e); } finally { this.manager.release(resolver); } // ContainerUtil.contextualize(selector, itsContext); // ContainerUtil.service(selector, this.itsManager); // ContainerUtil.configure(selector, config.getChild("nodes", false)); // ContainerUtil.initialize(selector); LifecycleHelper.setupComponent(selector, getLogger(), itsContext, this.itsManager, config.getChild("nodes", false)); this.itsBuilders = selector; } // Calls to getRegisteredNode() are forbidden this.canGetNode = false; // Collect all disposable variable resolvers VariableResolverFactory.setDisposableCollector(this.disposableNodes); ProcessingNode result = createTree(tree); // Calls to getRegisteredNode() are now allowed this.canGetNode = true; linkNodes(); // Initialize all Initializable nodes Iterator iter = this.initializableNodes.iterator(); while (iter.hasNext()) { ((Initializable) iter.next()).initialize(); } // And that's all ! return result; } /** * Return the list of <code>ProcessingNodes</code> part of this tree that * are <code>Disposable</code>. Care should be taken to properly dispose * them before trashing the processing tree. */ public List getDisposableNodes() { return this.disposableNodes; } /** * Setup a <code>ProcessingNode</code> by setting its location, calling * all the lifecycle interfaces it implements and giving it the parameter * map if it's a <code>ParameterizableNode</code>. * <p> * As a convenience, the node is returned by this method to allow constructs * like <code>return treeBuilder.setupNode(new MyNode(), config)</code>. */ public ProcessingNode setupNode(ProcessingNode node, Configuration config) throws Exception { Location location = getLocation(config); if (node instanceof AbstractProcessingNode) { ((AbstractProcessingNode) node).setLocation(location); ((AbstractProcessingNode) node).setSitemapExecutor(this.processor.getSitemapExecutor()); } this.itsLifecycle.setupComponent(node, false); if (node instanceof ParameterizableProcessingNode) { Map params = getParameters(config, location); ((ParameterizableProcessingNode) node).setParameters(params); } if (node instanceof Initializable) { this.initializableNodes.add(node); } if (node instanceof Disposable) { this.disposableNodes.add(node); } return node; } protected LocationImpl getLocation(Configuration config) { String prefix = ""; if (config instanceof AbstractConfiguration) { // FIXME: AbstractConfiguration has a _protected_ getPrefix() // method. // So make some reasonable guess on the prefix until it becomes // public String namespace = null; try { namespace = config.getNamespace(); } catch (ConfigurationException e) { // ignore } if ("http://apache.org/cocoon/sitemap/1.0".equals(namespace)) { prefix = "map"; } } StringBuffer desc = new StringBuffer().append('<'); if (prefix.length() > 0) { desc.append(prefix).append(':').append(config.getName()); } else { desc.append(config.getName()); } String type = config.getAttribute("type", null); if (type != null) { desc.append(" type=\"").append(type).append('"'); } desc.append('>'); Location rawLoc = LocationUtils.getLocation(config); return new LocationImpl(desc.toString(), rawLoc.getURI(), rawLoc.getLineNumber(), rawLoc.getColumnNumber()); } /** * Get <xxx:parameter> elements as a <code>Map</code> of </code>ListOfMapResolver</code>s, * that can be turned into parameters using <code>ListOfMapResolver.buildParameters()</code>. * * @return the Map of ListOfMapResolver, or <code>null</code> if there are * no parameters. */ protected Map getParameters(Configuration config, Location location) throws ConfigurationException { Configuration[] children = config.getChildren("parameter"); if (children.length == 0) { // Parameters are only the component's location // TODO Optimize this return new SitemapParameters.LocatedHashMap(location, 0); } Map params = new SitemapParameters.LocatedHashMap(location, children.length + 1); for (int i = 0; i < children.length; i++) { Configuration child = children[i]; if (true) { // FIXME : check namespace String name = child.getAttribute("name"); String value = child.getAttribute("value"); try { params.put(resolve(name), resolve(value)); } catch (PatternException pe) { String msg = "Invalid pattern '" + value + "' at " + child.getLocation(); throw new ConfigurationException(msg, pe); } } } return params; } /** * Get the type for a statement : it returns the 'type' attribute if * present, and otherwhise the default type defined for this role in the * components declarations. * * @throws ConfigurationException * if the type could not be found. */ public String getTypeForStatement(Configuration statement, String role) throws ConfigurationException { // Get the component type for the statement String type = statement.getAttribute("type", null); if (type == null) { type = this.itsComponentInfo.getDefaultType(role); } if (type == null) { throw new ConfigurationException( "No default type exists for 'map:" + statement.getName() + "' at " + statement.getLocation()); } final String beanName = role + '/' + type; if (!this.itsContainer.containsBean(beanName)) { throw new ConfigurationException("Type '" + type + "' does not exist for 'map:" + statement.getName() + "' at " + statement.getLocation()); } return type; } /** * Resolve expression using its manager */ protected VariableResolver resolve(String expression) throws PatternException { return VariableResolverFactory.getResolver(expression, this.itsManager); } public void recycle() { // Reset all data created during the build this.attributes.clear(); this.canGetNode = false; this.disposableNodes = new ArrayList(); // Must not be cleared as it's // used for processor disposal this.initializableNodes.clear(); this.linkedBuilders.clear(); this.processor = null; // Set in setProcessor() this.itsNamespace = null; // Set in build() LifecycleHelper.dispose(this.itsBuilders); this.itsBuilders = null; // Set in build() this.itsLifecycle = null; // Set in build() this.itsManager = null; // Set in build() this.registeredNodes.clear(); this.initializableNodes.clear(); this.linkedBuilders.clear(); this.canGetNode = false; this.registeredNodes.clear(); VariableResolverFactory.setDisposableCollector(null); this.enterSitemapEventListeners.clear(); this.leaveSitemapEventListeners.clear(); // Go back to initial state this.labelViews.clear(); this.viewsNode = null; this.isBuildingView = false; this.isBuildingErrorHandler = false; } /** * Register all registered sitemap listeners */ protected void registerListeners() { final ListableBeanFactory listableFactory = this.itsContainer; Map beans = listableFactory.getBeansOfType(EnterSitemapEventListener.class); if (beans != null) { final Iterator i = beans.values().iterator(); while (i.hasNext()) { this.enterSitemapEventListeners.add(i.next()); } } beans = listableFactory.getBeansOfType(LeaveSitemapEventListener.class); if (beans != null) { final Iterator i = beans.values().iterator(); while (i.hasNext()) { this.leaveSitemapEventListeners.add(i.next()); } } } // ---- Views management /** Collection of view names for each label */ private Map labelViews = new HashMap(); /** The views CategoryNode */ private CategoryNode viewsNode; /** Are we currently building a view ? */ private boolean isBuildingView = false; /** Are we currently building a view ? */ private boolean isBuildingErrorHandler = false; /** * Pseudo-label for views <code>from-position="first"</code> (i.e. * generator). */ public static final String FIRST_POS_LABEL = "!first!"; /** * Pseudo-label for views <code>from-position="last"</code> (i.e. * serializer). */ public static final String LAST_POS_LABEL = "!last!"; /** * Set to <code>true</code> while building the internals of a * <map:view> */ public void setBuildingView(boolean building) { this.isBuildingView = building; } /** * Are we currently building a view ? */ public boolean isBuildingView() { return this.isBuildingView; } /** * Set to <code>true</code> while building the internals of a * <map:handle-errors> */ public void setBuildingErrorHandler(boolean building) { this.isBuildingErrorHandler = building; } /** * Are we currently building an error handler ? */ public boolean isBuildingErrorHandler() { return this.isBuildingErrorHandler; } /** * Add a view for a label. This is used to register all views that start * from a given label. * * @param label * the label (or pseudo-label) for the view * @param view * the view name */ public void addViewForLabel(String label, String view) { if (getLogger().isDebugEnabled()) { getLogger().debug("views:addViewForLabel(" + label + ", " + view + ")"); } Set views = (Set) this.labelViews.get(label); if (views == null) { views = new HashSet(); this.labelViews.put(label, views); } views.add(view); } /** * Get the names of views for a given statement. If the cocoon view exists * in the returned collection, the statement can directly branch to the * view-handling node. * * @param role * the component role (e.g. <code>Generator.ROLE</code>) * @param hint * the component hint, i.e. the 'type' attribute * @param statement * the sitemap statement * @return the view names for this statement */ public Collection getViewsForStatement(String role, String hint, Configuration statement) throws Exception { String statementLabels = statement.getAttribute("label", null); if (this.isBuildingView) { // Labels are forbidden inside view definition if (statementLabels != null) { String msg = "Cannot put a 'label' attribute inside view definition at " + statement.getLocation(); throw new ConfigurationException(msg); } // We are currently building a view. Don't recurse ! return null; } // Compute the views attached to this component Set views; // Build the set for all labels for this statement Set labels = new HashSet(); // 1 - labels defined on the component if (role != null && role.length() > 0) { String[] compLabels = this.itsComponentInfo.getLabels(role, hint); if (compLabels != null) { for (int i = 0; i < compLabels.length; i++) { labels.add(compLabels[i]); } } } // 2 - labels defined on this statement if (statementLabels != null) { labels.addAll(splitLabels(statementLabels)); } // 3 - pseudo-label depending on the role if (Generator.ROLE.equals(role)) { labels.add("!first!"); } else if (Serializer.ROLE.equals(role)) { labels.add("!last!"); } // Build the set of views attached to these labels views = new HashSet(); // Iterate on all labels for this statement Iterator labelIter = labels.iterator(); while (labelIter.hasNext()) { // Iterate on all views for this labek Collection coll = (Collection) this.labelViews.get(labelIter.next()); if (coll != null) { Iterator viewIter = coll.iterator(); while (viewIter.hasNext()) { String viewName = (String) viewIter.next(); views.add(viewName); } } } // Don't keep empty result if (views.size() == 0) { views = null; if (getLogger().isDebugEnabled()) { getLogger().debug(statement.getName() + " has no views at " + statement.getLocation()); } } else { if (getLogger().isDebugEnabled()) { // Dump matching views StringBuffer buf = new StringBuffer(statement.getName() + " will match views ["); Iterator iter = views.iterator(); while (iter.hasNext()) { buf.append(iter.next()).append(" "); } buf.append("] at ").append(statement.getLocation()); getLogger().debug(buf.toString()); } } return views; } /** * Get the {view name, view node} map for a collection of view names. This * allows to resolve view nodes at build time, thus avoiding runtime lookup. * * @param viewNames * the view names * @return association of names to views */ public Map getViewNodes(Collection viewNames) throws Exception { if (viewNames == null || viewNames.size() == 0) { return null; } if (this.viewsNode == null) { return null; } Map result = new HashMap(); Iterator iter = viewNames.iterator(); while (iter.hasNext()) { String viewName = (String) iter.next(); result.put(viewName, viewsNode.getNodeByName(viewName)); } return result; } /** * Extract pipeline-hints from the given statement (if any exist) * * @param role * the component role (e.g. <code>Generator.ROLE</code>) * @param hint * the component hint, i.e. the 'type' attribute * @param statement * the sitemap statement * @return the hint params <code>Map</code> for this statement, or null if * none exist */ public Map getHintsForStatement(String role, String hint, Configuration statement) throws Exception { // This method implemets the hintParam Syntax as follows: // A hints attribute has one or more comma separated hints // hints-attr :: hint [ ',' hint ]* // A hint is a name and an optional (string) value // If there is no value, it is considered as boolean string "true" // hint :: literal [ '=' litteral ] // literal :: <a character string where the chars ',' and '=' are not // permitted> // // A ConfigurationException is thrown if there is a problem "parsing" // the hint. String statementHintParams = statement.getAttribute("pipeline-hints", null); // firstly, determine if any pipeline-hints are defined at the component // level // if so, inherit these pipeline-hints (these hints can be overriden by // local pipeline-hints) String componentHintParams = this.itsComponentInfo.getPipelineHint(role, hint); String hintParams; if (componentHintParams != null) { hintParams = componentHintParams; if (statementHintParams != null) { hintParams = hintParams + "," + statementHintParams; } } else { hintParams = statementHintParams; } // if there are no pipeline-hints defined then // it makes no sense to continue so, return null if (hintParams == null) { return null; } Map params = new HashMap(); RE commaSplit = new RE(COMMA_SPLIT_REGEXP); RE equalsSplit = new RE(EQUALS_SPLIT_REGEXP); String[] expressions = commaSplit.split(hintParams.trim()); if (getLogger().isDebugEnabled()) { getLogger().debug("pipeline-hints: (aggregate-hint) " + hintParams); } for (int i = 0; i < expressions.length; i++) { String[] nameValuePair = equalsSplit.split(expressions[i]); try { if (nameValuePair.length < 2) { if (getLogger().isDebugEnabled()) { getLogger().debug("pipeline-hints: (name) " + nameValuePair[0] + "\npipeline-hints: (value) [implicit] true"); } params.put(resolve(nameValuePair[0]), resolve("true")); } else { if (getLogger().isDebugEnabled()) { getLogger().debug("pipeline-hints: (name) " + nameValuePair[0] + "\npipeline-hints: (value) " + nameValuePair[1]); } params.put(resolve(nameValuePair[0]), resolve(nameValuePair[1])); } } catch (PatternException pe) { String msg = "Invalid pattern '" + hintParams + "' at " + statement.getLocation(); getLogger().error(msg, pe); throw new ConfigurationException(msg, pe); } } return params; } /** * Get the mime-type for a component (either a serializer or a reader) * * @param role the component role (e.g. <code>Serializer.ROLE</code>) * @param hint the component hint, i.e. the 'type' attribute * @return the mime-type, or <code>null</code> if none was set */ public String getMimeType(String role, String hint) { return this.itsComponentInfo.getMimeType(role, hint); } /** * Split a list of space/comma separated labels into a Collection * * @return the collection of labels (may be empty, nut never null) */ private static Collection splitLabels(String labels) { if (labels == null) { return Collections.EMPTY_SET; } return Arrays.asList(StringUtils.split(labels, ", \t\n\r")); } }