/*
* Copyright 2004-2012 the Seasar Foundation and the Others.
*
* Licensed 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.seasar.mayaa.impl.engine.processor;
import org.seasar.mayaa.builder.PathAdjuster;
import org.seasar.mayaa.cycle.script.CompiledScript;
import org.seasar.mayaa.engine.Page;
import org.seasar.mayaa.engine.processor.ProcessStatus;
import org.seasar.mayaa.engine.processor.ProcessorProperty;
import org.seasar.mayaa.engine.processor.ProcessorTreeWalker;
import org.seasar.mayaa.engine.specification.PrefixAwareName;
import org.seasar.mayaa.engine.specification.QName;
import org.seasar.mayaa.impl.cycle.CycleUtil;
import org.seasar.mayaa.impl.engine.EngineUtil;
import org.seasar.mayaa.impl.engine.specification.SpecificationUtil;
import org.seasar.mayaa.impl.provider.ProviderUtil;
import org.seasar.mayaa.impl.util.StringUtil;
/**
* @author Masataka Kurihara (Gluegent, Inc.)
*/
public class AttributeProcessor extends TemplateProcessorSupport {
private static final long serialVersionUID = 87560642282310502L;
private PrefixAwareName _name;
private ProcessorProperty _value;
private ProcessorProperty _escapeAmp;
protected AbstractAttributableProcessor findParentAttributable() {
for (ProcessorTreeWalker parent = getParentProcessor();
parent != null;
parent = parent.getParentProcessor()) {
if (parent instanceof AbstractAttributableProcessor) {
if (parent instanceof ElementProcessor) {
// duplicated ElementProcessor is not attributable.
if (((ElementProcessor) parent).isDuplicated()) {
continue;
}
}
return (AbstractAttributableProcessor) parent;
}
}
throw new IllegalStateException("no attributable processor.");
}
// MLD property
public void setName(PrefixAwareName name) {
if (name == null) {
throw new IllegalArgumentException();
}
_name = name;
}
protected PrefixAwareName getName() {
return _name;
}
// MLD property
public void setValue(ProcessorProperty value) {
if (value == null) {
throw new IllegalArgumentException();
}
_value = value;
}
// MLD property
public void setEscapeAmp(ProcessorProperty escapeAmp) {
ProcessorUtil.checkBoolableProperty(escapeAmp);
_escapeAmp = escapeAmp;
}
boolean isEscapeAmp() {
return ProcessorUtil.toBoolean(_escapeAmp);
}
/**
* 親プロセッサの{@link QName}を返す。ただし
* {@code parent}が{@link ElementProcessor}の場合にはそのnameを返す。
* @param parent 親プロセッサ
* @return 親プロセッサの{@link QName}。
*/
protected QName getParentQName(AbstractAttributableProcessor parent) {
QName parentQName;
if (parent.getClass() == ElementProcessor.class) {
parentQName = ((ElementProcessor) parent).getName().getQName();
} else {
parentQName = parent.getOriginalNode().getQName();
}
return parentQName;
}
protected ProcessorProperty createProcessorProperty(PrefixAwareName name, ProcessorProperty value, String basePath) {
return new ProcessorPropertyWrapper(_name, _value, basePath);
}
public ProcessStatus doStartProcess(Page topLevelPage) {
/* 値なし属性もサポート(ただし非推奨のため、mayaa.mldのrequiredを外さない限りこの機能は無効。)
if (_value == null) {
throw new IllegalStateException();
}
*/
AbstractAttributableProcessor parent = findParentAttributable();
QName parentQName = getParentQName(parent);
QName attributeQName = getName().getQName();
// 自動的にmayaaネームスペースを引き継いだだけであれば、親要素と同じにする。
if (getName().getPrefix().equals("")
&& parentQName.equals(getName().getQName()) == false) {
attributeQName = SpecificationUtil.createQName(
parentQName.getNamespaceURI(),
attributeQName.getLocalName());
setName(SpecificationUtil.createPrefixAwareName(attributeQName, ""));
}
String basePath = null;
PathAdjuster adjuster = ProviderUtil.getPathAdjuster();
if (adjuster.isTargetAttribute(parentQName, attributeQName)) {
String contextPath = CycleUtil.getRequestScope().getContextPath();
String sourcePath = EngineUtil.getSourcePath(getStaticParentProcessor());
basePath = contextPath + sourcePath;
}
parent.addProcesstimeProperty(createProcessorProperty(_name, _value, basePath));
return ProcessStatus.SKIP_BODY;
}
// support class ------------------------------------------------
protected class ProcessorPropertyWrapper implements ProcessorProperty {
private static final long serialVersionUID = 8190328285510500572L;
private PrefixAwareName _attrName;
private ProcessorProperty _attrValue;
private CompiledScript _script;
public ProcessorPropertyWrapper(
PrefixAwareName name, ProcessorProperty property, String basePath) {
if (name == null) {
throw new IllegalArgumentException();
}
_attrName = name;
_attrValue = property;
if (_attrValue != null) {
if (_attrValue.getValue().isLiteral()) {
_script = new EscapedLiteralScript(_attrValue.getValue(), basePath);
} else {
_script = new EscapableScript(_attrValue.getValue(), basePath);
}
}
}
public PrefixAwareName getName() {
return _attrName;
}
public CompiledScript getValue() {
return _script;
}
public boolean equals(Object obj) {
if (obj instanceof ProcessorProperty) {
PrefixAwareName otherName =
((ProcessorProperty) obj).getName();
return getName().getQName().equals(otherName.getQName());
}
return false;
}
public int hashCode() {
return toString().hashCode();
}
public String toString() {
if (_script == null) {
return getName().toString() + "=null";
}
return getName().toString() + "=\"" + _script + "\"";
}
}
protected class EscapableScript extends ScriptWrapper {
private static final long serialVersionUID = -5393294025521796857L;
private String _basePath;
public EscapableScript(CompiledScript script, String basePath) {
super(script);
_basePath = basePath;
}
public Object execute(Object[] args) {
Object result = super.execute(args);
if (isString() && StringUtil.hasValue(result)) {
if (_basePath != null) {
PathAdjuster adjuster = ProviderUtil.getPathAdjuster();
result =
adjuster.adjustRelativePath(_basePath, result.toString());
}
result = escape(result.toString());
}
return result;
}
}
protected class EscapedLiteralScript extends ScriptWrapper {
private static final long serialVersionUID = -441522603771461865L;
private String _escapedValue = "";
public EscapedLiteralScript(CompiledScript script, String basePath) {
super(script);
if (isString()) {
Object obj = super.execute(null);
if (StringUtil.hasValue(obj)) {
_escapedValue = obj.toString();
if (basePath != null) {
PathAdjuster adjuster = ProviderUtil.getPathAdjuster();
_escapedValue =
adjuster.adjustRelativePath(
basePath, _escapedValue);
}
_escapedValue = escape(_escapedValue);
}
}
}
public Object execute(Object[] args) {
return _escapedValue;
}
}
protected abstract class ScriptWrapper implements CompiledScript {
private static final long serialVersionUID = -1582289477400400196L;
private CompiledScript _script;
private boolean _string;
public ScriptWrapper(CompiledScript script) {
_script = script;
_string = String.class.equals(_script.getExpectedClass());
}
public boolean isString() {
return _string;
}
public void setExpectedClass(Class expectedClass) {
_script.setExpectedClass(expectedClass);
}
public Class getExpectedClass() {
return _script.getExpectedClass();
}
public Object execute(Object[] args) {
return _script.execute(args);
}
public void setMethodArgClasses(Class[] methodArgClasses) {
_script.setMethodArgClasses(methodArgClasses);
}
public Class[] getMethodArgClasses() {
return _script.getMethodArgClasses();
}
public boolean isLiteral() {
return _script.isLiteral();
}
public String getScriptText() {
return _script.getScriptText();
}
public boolean isReadOnly() {
return _script.isReadOnly();
}
public void assignValue(Object value) {
_script.assignValue(value);
}
public String toString() {
return _script.toString();
}
public String escape(String value) {
if (isEscapeAmp()) {
return StringUtil.escapeWhitespace(StringUtil.escapeXml(value));
}
return StringUtil.escapeWhitespace(StringUtil.escapeXmlWithoutAmp(value));
}
}
}