package org.anodyneos.xpImpl.translater;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.anodyneos.commons.xml.sax.ElementProcessor;
import org.anodyneos.xp.tagext.TagAttributeInfo;
import org.anodyneos.xp.tagext.TagInfo;
import org.anodyneos.xpImpl.util.CodeWriter;
import org.anodyneos.xpImpl.util.Util;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.NamespaceSupport;
/**
* ProcessorTag handles custom Tags
*
* <li>startElement: instantiate tag. Validate names and types of attributes
* vs TLD, call setters.
*
* <li>getProcessorFor: if xp:attribute, get processor that can handle
* attributes for regular content or fragments. If anything else, this the
* first element in a fragment, so create fragment and get new
* ProcessorResultContent.
*
* <li>characters: may be useless whitespace, tag-specific text content, or
* part of a fragment. For now, populate StringBuffer for later use.
*
* @author jvas
*/
public class ProcessorTag extends HelperProcessorNonResultContent {
private StringBuffer sb;
private ProcessorResultContent bodyFragmentProcessor;
private Map bodyFragmentPrefixMap;
private boolean bodyFragmentStarted = false;
int bodyFragmentId = -1;
Set handledAttributes = new HashSet();
TagInfo tagInfo = null;
Map attributeInfos = null;
String localVarName = null;
public static final String E_ATTRIBUTE = "attribute";
public ProcessorTag(TranslaterContext ctx) {
super(ctx);
bodyFragmentProcessor = new ProcessorResultContent(ctx);
// when we write the fragment, we will need to copy the source tree's prefix mappings into the output
// tree in case the fragment is used outside of the immediate parent in the source tree.
bodyFragmentPrefixMap = new HashMap();
NamespaceSupport ns = getTranslaterContext().getNamespaceSupport();
Enumeration e = ns.getPrefixes();
while (e.hasMoreElements()) {
String nsPrefix = (String) e.nextElement();
String nsURI = ns.getURI(nsPrefix);
bodyFragmentPrefixMap.put(nsPrefix, nsURI);
}
String uri = ns.getURI("");
if(null == uri) {
uri = "";
}
bodyFragmentPrefixMap.put("", uri);
}
public ElementProcessor getProcessorFor(String uri, String localName, String qName)
throws SAXException {
// TODO: consider xp:tagAttribute to allow subelements to provide values for tag attributes.
ElementProcessor p = bodyFragmentProcessor.getProcessorFor(uri, localName, qName);
// since no exception, start fragment if not already started. Dump characters.
// Return p (p may be the bodyFragmentProcessor itself if the content is result
// content, but bodyFragmentProcessor made the decision.)
if (! bodyFragmentStarted) {
startBodyFragment();
bodyFragmentStarted = true;
}
if(null != sb) {
char[] chars = sb.toString().toCharArray();
bodyFragmentProcessor.characters(chars, 0, chars.length);
sb = null;
// make sure to flush characters in processor
bodyFragmentProcessor.flushCharacters();
}
return p;
}
public void startElementNonResultContent(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
TranslaterContext ctx = getTranslaterContext();
CodeWriter out = ctx.getCodeWriter();
this.tagInfo = ctx.getTagLibraryRegistry().getTagLibraryInfo(uri).getTagInfo(localName);
this.attributeInfos = toMap(tagInfo.getTagAttributeInfos());
String tagImplClass = tagInfo.getClassName();
this.localVarName = ctx.getVariableForTag(tagImplClass);
// instantiate tag
out.printIndent().println(tagImplClass + " " + localVarName + " = new " + tagImplClass + "();");
if(ctx.inFragment()) {
out.printIndent().println(localVarName + ".setParent(xpTagParent);");
} else {
out.printIndent().println("// " + localVarName + ".setParent(null); // no parent tag");
}
out.printIndent().println(localVarName + ".setXpContext(xpContext);");
// add attributes
for (int i = 0; i < attributes.getLength(); i++) {
String prefix = parsePrefix(attributes.getQName(i));
String name = parseLocalName(attributes.getQName(i));
if (prefix.length() == 0 && ! name.equals("xmlns")) {
String value = attributes.getValue(i);
TagAttributeInfo attrInfo = (TagAttributeInfo) attributeInfos.get(name);
if (null == attrInfo) {
throw new SAXException("attribute '" + name + "' not allowed in tag " + qName);
}
// add attribute
String codeValue;
if (Util.hasEL(value) && ! attrInfo.isRequestTimeOK()) {
throw new SAXException("attribute '" + name + "' cannot have EL");
} else {
codeValue = Util.elExpressionCode(value, attrInfo.getType());
}
out.printIndent().println(localVarName + "." + Util.toSetMethod(name) + "(" + codeValue + ");");
this.handledAttributes.add(name);
}
}
}
public void characters(char[] ch, int start, int length) {
if (null == sb) {
sb = new StringBuffer();
}
sb.append(ch, start, length);
}
public void endElementNonResultContent(String uri, String localName, String qName) throws SAXException {
// end element
if (null != sb && ! sb.toString().trim().equals("")) {
if (! bodyFragmentStarted) {
startBodyFragment();
bodyFragmentStarted = true;
}
char[] chars = sb.toString().toCharArray();
bodyFragmentProcessor.characters(chars, 0, chars.length);
sb = null;
// make sure to flush characters in processor
bodyFragmentProcessor.flushCharacters();
}
if (bodyFragmentStarted) {
bodyFragmentProcessor.flushCharacters();
CodeWriter out = getTranslaterContext().getCodeWriter();
out.println();
out.printIndent().println("if (! namespaceCompat) {");
out.indentPlus();
Iterator it = bodyFragmentPrefixMap.keySet().iterator();
for(int i = bodyFragmentPrefixMap.size(); i > 0; i--) {
String nsPrefix = (String) it.next();
String nsURI = (String) bodyFragmentPrefixMap.get(nsPrefix);
out.printIndent().println("xpCH.popPhantomPrefixMapping();");
}
out.endBlock();
getTranslaterContext().endFragment();
out = getTranslaterContext().getCodeWriter();
out.printIndent().println(
localVarName + ".setXpBody(new FragmentHelper(" + bodyFragmentId + ", xpContext, " + localVarName + ", xpCH));");
}
CodeWriter out = getTranslaterContext().getCodeWriter();
out.printIndent().println(localVarName + ".doTag(xpOut);");
out.printIndent().println(localVarName + " = null;");
}
private Map toMap(TagAttributeInfo[] infos) {
Map map = new HashMap();
if (null != infos) {
for(int i = 0; i < infos.length; i++) {
map.put(infos[i].getName(), infos[i]);
}
}
return map;
}
private void startBodyFragment() {
assert (! bodyFragmentStarted);
bodyFragmentId = getTranslaterContext().startFragment();
CodeWriter out = getTranslaterContext().getCodeWriter();
// check to see if we need to declare our prefixes
out.printIndent().println("boolean namespaceCompat = xpCH.isNamespaceContextCompatible(origXpCH, parentElClosed, origContextVersion, origAncestorsWithPrefixMasking, origPhantomPrefixCount);");
out.printIndent().println("if (! namespaceCompat) {");
out.indentPlus();
Iterator it = bodyFragmentPrefixMap.keySet().iterator();
while (it.hasNext()) {
String nsPrefix = (String) it.next();
String nsURI = (String) bodyFragmentPrefixMap.get(nsPrefix);
out.printIndent().println(
"xpCH.pushPhantomPrefixMapping("
+ Util.escapeStringQuoted(nsPrefix)
+ ", " + Util.escapeStringQuoted(nsURI)
+ ");");
}
out.endBlock();
out.println();
}
private static final String parseLocalName(String qName) {
if (null == qName || qName.length() == 0) {
return "";
} else {
int colon = qName.indexOf(':');
if (-1 == colon) {
return qName;
} else {
return qName.substring(colon + 1);
}
}
}
private static final String parsePrefix(String qName) {
if (null == qName || qName.length() == 0) {
return "";
} else {
int colon = qName.indexOf(':');
if (-1 == colon) {
return "";
} else {
return qName.substring(0, colon);
}
}
}
}