| /* |
| * 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.template.instruction; |
| |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Stack; |
| |
| import org.apache.cocoon.components.expression.ExpressionContext; |
| import org.apache.cocoon.template.environment.ErrorHolder; |
| import org.apache.cocoon.template.environment.ExecutionContext; |
| import org.apache.cocoon.template.environment.ParsingContext; |
| import org.apache.cocoon.template.expression.JXTExpression; |
| import org.apache.cocoon.template.script.Invoker; |
| import org.apache.cocoon.template.script.event.AttributeEvent; |
| import org.apache.cocoon.template.script.event.Characters; |
| import org.apache.cocoon.template.script.event.Event; |
| import org.apache.cocoon.template.script.event.IgnorableWhitespace; |
| import org.apache.cocoon.template.script.event.StartElement; |
| import org.apache.cocoon.template.script.event.TextEvent; |
| import org.apache.cocoon.xml.XMLConsumer; |
| import org.apache.commons.lang.StringUtils; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.Locator; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| |
| /** |
| * @version $Id$ |
| */ |
| public class Call extends Instruction { |
| |
| private Object macro; |
| private JXTExpression targetNamespace; |
| private Map parameters; |
| private Event body; |
| |
| public Call(Define definition, StartElement startElement) |
| throws SAXException { |
| super(startElement); |
| this.parameters = new HashMap(); |
| setBody(startElement); |
| setNext(startElement.getNext()); |
| setDefinition(definition); |
| |
| Iterator i = startElement.getAttributeEvents().iterator(); |
| while (i.hasNext()) { |
| AttributeEvent attrEvent = (AttributeEvent) i.next(); |
| addParameterInstance(attrEvent); |
| } |
| } |
| |
| public Call(ParsingContext parsingContext, StartElement raw, Attributes attrs, Stack stack) |
| throws SAXException { |
| super(raw); |
| this.parameters = new HashMap(); |
| Locator locator = getLocation(); |
| |
| String name = attrs.getValue("macro"); |
| if (name == null) { |
| throw new SAXParseException("if: \"test\" is required", locator, |
| null); |
| } |
| this.macro = parsingContext.getStringTemplateParser().compileExpr(name, "call: \"macro\": ", |
| locator); |
| |
| String namespace = StringUtils.defaultString(attrs |
| .getValue("targetNamespace")); |
| this.targetNamespace = parsingContext.getStringTemplateParser().compileExpr(namespace, |
| "call: \"targetNamespace\": ", locator); |
| } |
| |
| public void setDefinition(Define definition) { |
| this.macro = definition; |
| } |
| |
| public void addParameterInstance(AttributeEvent attributeEvent) |
| throws SAXException { |
| ParameterInstance parameter = new ParameterInstance( |
| attributeEvent); |
| this.parameters.put(parameter.getName(), parameter); |
| } |
| |
| public Event execute(XMLConsumer consumer, |
| ExpressionContext expressionContext, |
| ExecutionContext executionContext, MacroContext macroContext, |
| Event startEvent, Event endEvent) throws SAXException { |
| Map attributeMap = new HashMap(); |
| Iterator i = parameters.keySet().iterator(); |
| while (i.hasNext()) { |
| String parameterName = (String) i.next(); |
| ParameterInstance parameter = (ParameterInstance) parameters |
| .get(parameterName); |
| Object parameterValue = parameter.getValue(expressionContext); |
| attributeMap.put(parameterName, parameterValue); |
| } |
| ExpressionContext localExpressionContext = new ExpressionContext( |
| expressionContext); |
| HashMap macro = new HashMap(); |
| macro.put("body", this.body); |
| macro.put("arguments", attributeMap); |
| localExpressionContext.put("macro", macro); |
| |
| Define definition = resolveMacroDefinition(expressionContext, |
| executionContext); |
| Iterator iter = definition.getParameters().entrySet().iterator(); |
| while (iter.hasNext()) { |
| Map.Entry e = (Map.Entry) iter.next(); |
| String key = (String) e.getKey(); |
| Parameter startParam = (Parameter) e.getValue(); |
| Object default_ = startParam.getDefaultValue(); |
| Object val = attributeMap.get(key); |
| if (val == null) { |
| val = default_; |
| } |
| localExpressionContext.put(key, val); |
| } |
| |
| Event macroBodyStart = getNext(); |
| Event macroBodyEnd = null; |
| |
| if (getEndInstruction() != null) |
| macroBodyEnd = getEndInstruction(); |
| else |
| macroBodyEnd = getStartElement().getEndElement(); |
| |
| MacroContext newMacroContext = new MacroContext(definition.getQname(), |
| macroBodyStart, macroBodyEnd); |
| try { |
| Invoker.execute(consumer, localExpressionContext, executionContext, |
| newMacroContext, definition.getBody(), definition |
| .getEndInstruction()); |
| } catch (SAXParseException exc) { |
| throw new SAXParseException(newMacroContext.getMacroQName() + ": " |
| + exc.getMessage(), location, exc); |
| } |
| |
| if (getEndInstruction() != null) |
| return getEndInstruction().getNext(); |
| else |
| return getStartElement().getEndElement().getNext(); |
| } |
| |
| /** |
| * @param executionContext |
| * @throws SAXParseException |
| */ |
| private Define resolveMacroDefinition( |
| ExpressionContext expressionContext, |
| ExecutionContext executionContext) throws SAXParseException { |
| if (this.macro instanceof Define) |
| return (Define) macro; |
| |
| Object macroName; |
| Object namespace; |
| JXTExpression macroNameExpression = (JXTExpression) macro; |
| try { |
| macroName = macroNameExpression.getValue(expressionContext); |
| namespace = targetNamespace.getValue(expressionContext); |
| if (namespace == null) |
| namespace = ""; |
| } catch (Exception e) { |
| throw new SAXParseException(e.getMessage(), getLocation(), e); |
| } catch (Error err) { |
| throw new SAXParseException(err.getMessage(), getLocation(), |
| new ErrorHolder(err)); |
| } |
| Define definition = (Define) executionContext |
| .getDefinitions() |
| .get("{" + namespace.toString() + "}" + macroName.toString()); |
| if (definition == null) |
| throw new SAXParseException("no macro definition: " + macroName, |
| getLocation()); |
| return definition; |
| } |
| |
| /** |
| * @param body |
| */ |
| public void setBody(Event body) { |
| this.body = body; |
| |
| } |
| |
| public void endNotify() throws SAXException { |
| // FIXME: copy/pasted from StartDefine (almost) |
| Event e = next; |
| boolean params = true; |
| while (e != this.getEndInstruction()) { |
| if (e instanceof ParameterInstance) { |
| ParameterInstance startParamInstance = (ParameterInstance) e; |
| if (!params) { |
| throw new SAXParseException( |
| "<parameter value> not allowed here: \"" |
| + startParamInstance.name + "\"", |
| startParamInstance.getLocation(), null); |
| } |
| Object prev = this.parameters.put(startParamInstance.name, |
| startParamInstance); |
| if (prev != null) { |
| throw new SAXParseException("duplicate parameter value: \"" |
| + startParamInstance.name + "\"", location, null); |
| } |
| e = startParamInstance.getEndInstruction(); |
| } else if (e instanceof IgnorableWhitespace) { |
| // EMPTY |
| } else if (e instanceof Characters) { |
| // check for whitespace |
| char[] ch = ((TextEvent) e).getRaw(); |
| int len = ch.length; |
| for (int i = 0; i < len; i++) { |
| if (!Character.isWhitespace(ch[i])) { |
| if (params) { |
| params = false; |
| this.body = e; |
| } |
| break; |
| } |
| } |
| } else { |
| if (params) { |
| params = false; |
| this.body = e; |
| } |
| } |
| e = e.getNext(); |
| } |
| if (this.body == null) { |
| this.body = this.getEndInstruction(); |
| } |
| setNext(this.body); |
| } |
| } |