| /* |
| * 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.myfaces.view.facelets.compiler; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Stack; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import javax.faces.application.FacesMessage; |
| import javax.faces.view.facelets.FaceletHandler; |
| import javax.faces.view.facelets.Tag; |
| import javax.faces.view.facelets.TagAttribute; |
| import javax.faces.view.facelets.TagAttributeException; |
| import javax.faces.view.facelets.TagDecorator; |
| import javax.faces.view.facelets.TagException; |
| |
| import org.apache.myfaces.view.facelets.tag.TagAttributesImpl; |
| import org.apache.myfaces.view.facelets.tag.TagLibrary; |
| import org.apache.myfaces.view.facelets.tag.composite.CompositeLibrary; |
| import org.apache.myfaces.view.facelets.tag.composite.ImplementationHandler; |
| import org.apache.myfaces.view.facelets.tag.composite.InterfaceHandler; |
| import org.apache.myfaces.view.facelets.tag.ui.ComponentRefHandler; |
| import org.apache.myfaces.view.facelets.tag.ui.CompositionHandler; |
| import org.apache.myfaces.view.facelets.tag.ui.UILibrary; |
| |
| /** |
| * Compilation unit for managing the creation of a single FaceletHandler based on events from an XML parser. |
| * |
| * @see org.apache.myfaces.view.facelets.compiler.Compiler |
| * |
| * @author Jacob Hookom |
| * @version $Id$ |
| */ |
| final class CompilationManager |
| { |
| |
| private final static Logger log = Logger.getLogger(CompilationManager.class.getName()); |
| |
| private final Compiler compiler; |
| |
| private final TagLibrary tagLibrary; |
| |
| private final TagDecorator tagDecorator; |
| |
| private final NamespaceManager namespaceManager; |
| |
| private final Stack<CompilationUnit> units; |
| |
| private int tagId; |
| |
| private boolean finished; |
| |
| private final String alias; |
| |
| private CompilationUnit interfaceCompilationUnit; |
| |
| private final FaceletsProcessingInstructions faceletsProcessingInstructions; |
| |
| public CompilationManager(String alias, Compiler compiler, FaceletsProcessingInstructions instructions) |
| { |
| |
| // this is our alias |
| this.alias = alias; |
| |
| // grab compiler state |
| this.compiler = compiler; |
| this.tagDecorator = compiler.createTagDecorator(); |
| this.tagLibrary = compiler.createTagLibrary(); |
| |
| // namespace management |
| this.namespaceManager = new NamespaceManager(); |
| |
| // tag uids |
| this.tagId = 0; |
| |
| // for composition use |
| this.finished = false; |
| |
| // our compilationunit stack |
| this.units = new Stack<CompilationUnit>(); |
| this.units.push(new CompilationUnit()); |
| |
| this.interfaceCompilationUnit = null; |
| this.faceletsProcessingInstructions = instructions; |
| } |
| |
| public void writeInstruction(String value) |
| { |
| if (this.finished) |
| { |
| return; |
| } |
| |
| // don't carelessly add empty tags |
| if (value.length() == 0) |
| { |
| return; |
| } |
| |
| TextUnit unit; |
| if (this.currentUnit() instanceof TextUnit) |
| { |
| unit = (TextUnit) this.currentUnit(); |
| } |
| else |
| { |
| unit = new TextUnit(this.alias, this.nextTagId(), |
| faceletsProcessingInstructions.isEscapeInlineText(), |
| faceletsProcessingInstructions.isCompressSpaces()); |
| this.startUnit(unit); |
| } |
| unit.writeInstruction(value); |
| } |
| |
| public void writeDoctype(String name, String publicId, String systemId) |
| { |
| if (this.finished) |
| { |
| return; |
| } |
| |
| DoctypeUnit unit = new DoctypeUnit(this.alias, this.nextTagId(), |
| name, publicId, systemId, faceletsProcessingInstructions.isHtml5Doctype()); |
| this.startUnit(unit); |
| } |
| |
| public void writeText(String value) |
| { |
| |
| if (this.finished) |
| { |
| return; |
| } |
| |
| // don't carelessly add empty tags |
| if (value.length() == 0) |
| { |
| return; |
| } |
| |
| TextUnit unit; |
| if (this.currentUnit() instanceof TextUnit) |
| { |
| unit = (TextUnit) this.currentUnit(); |
| } |
| else |
| { |
| unit = new TextUnit(this.alias, this.nextTagId(), |
| faceletsProcessingInstructions.isEscapeInlineText(), |
| faceletsProcessingInstructions.isCompressSpaces()); |
| this.startUnit(unit); |
| } |
| unit.write(value); |
| } |
| |
| public void writeComment(String text) |
| { |
| if (this.compiler.isTrimmingComments()) |
| { |
| return; |
| } |
| |
| if (this.finished) |
| { |
| return; |
| } |
| |
| // don't carelessly add empty tags |
| if (text.length() == 0) |
| { |
| return; |
| } |
| |
| TextUnit unit; |
| if (this.currentUnit() instanceof TextUnit) |
| { |
| unit = (TextUnit) this.currentUnit(); |
| } |
| else |
| { |
| unit = new TextUnit(this.alias, this.nextTagId(), |
| faceletsProcessingInstructions.isEscapeInlineText(), |
| faceletsProcessingInstructions.isCompressSpaces()); |
| this.startUnit(unit); |
| } |
| |
| unit.writeComment(text); |
| } |
| |
| public void writeWhitespace(String text) |
| { |
| if (!this.compiler.isTrimmingWhitespace()) |
| { |
| this.writeText(text); |
| } |
| } |
| |
| private String nextTagId() |
| { |
| return Integer.toHexString(Math.abs(this.alias.hashCode() ^ 13 * this.tagId++)); |
| } |
| |
| public void pushTag(Tag orig) |
| { |
| |
| if (this.finished) |
| { |
| return; |
| } |
| |
| if (log.isLoggable(Level.FINE)) |
| { |
| log.fine("Tag Pushed: " + orig); |
| } |
| |
| Tag t = this.tagDecorator.decorate(orig); |
| String[] qname = this.determineQName(t); |
| t = this.trimAttributes(t); |
| |
| if (isTrimmed(qname[0], qname[1])) |
| { |
| log.fine("Composition Found, Popping Parent Tags"); |
| this.units.clear(); |
| NamespaceUnit nsUnit = this.namespaceManager.toNamespaceUnit(this.tagLibrary); |
| this.units.push(nsUnit); |
| this.startUnit(new TrimmedTagUnit(this.tagLibrary, qname[0], qname[1], t, this.nextTagId())); |
| log.fine("New Namespace and [Trimmed] TagUnit pushed"); |
| } |
| else if (isRemove(qname[0], qname[1])) |
| { |
| this.units.push(new RemoveUnit()); |
| } |
| else if (isCompositeComponentInterface(qname[0], qname[1])) |
| { |
| // Here we have two cases when we found a <composite:interface> tag: |
| // |
| // - If a page has a <composite:interface> tag and a <composite:implementation> tag. |
| // In this case, we need to trim all tags outside this two tags, otherwise these |
| // unwanted tags will be added when the composite component is applied. |
| // Unfortunately, this is the only point we can do it, because after the compiler, |
| // html tags are wrapped on facelets UIInstruction or UIText components as "list", |
| // losing the original structure required to trim. |
| // |
| // - If a page has a <composite:interface> tag and not a <composite:implementation> tag. |
| // In this case, it is not necessary to trim, because we use the facelet only to create |
| // metadata and the component tree created is not used (see |
| // ViewDeclarationLanguage.getComponentMetadata() ). On InterfaceHandler, instead |
| // there is some code that found the right component in the temporal tree to add the |
| // generated BeanInfo, which it is retrieved later. |
| // |
| // After use Template Client API for composite components, it was found the need to |
| // gather metadata information from |
| log.fine("Composite Component Interface Found, saving unit"); |
| CompositeComponentUnit compositeRootCompilationUnit = new CompositeComponentUnit(); |
| this.startUnit(compositeRootCompilationUnit); |
| interfaceCompilationUnit = new TagUnit(this.tagLibrary, qname[0], qname[1], t, this.nextTagId()); |
| this.startUnit(interfaceCompilationUnit); |
| } |
| else if (isCompositeComponentImplementation(qname[0], qname[1])) |
| { |
| log.fine("Composite component Found, Popping Parent Tags"); |
| this.units.clear(); |
| NamespaceUnit nsUnit = this.namespaceManager.toNamespaceUnit(this.tagLibrary); |
| this.units.push(nsUnit); |
| CompositeComponentUnit compositeRootCompilationUnit = new CompositeComponentUnit(); |
| this.startUnit(compositeRootCompilationUnit); |
| if (interfaceCompilationUnit != null) |
| { |
| this.currentUnit().addChild(interfaceCompilationUnit); |
| interfaceCompilationUnit = null; |
| } |
| this.startUnit(new TrimmedTagUnit(this.tagLibrary, qname[0], qname[1], t, this.nextTagId())); |
| log.fine("New Namespace and TagUnit pushed"); |
| } |
| else if (this.tagLibrary.containsTagHandler(qname[0], qname[1])) |
| { |
| this.startUnit(new TagUnit(this.tagLibrary, qname[0], qname[1], t, this.nextTagId())); |
| } |
| else if (this.tagLibrary.containsNamespace(qname[0])) |
| { |
| throw new TagException(orig, "Tag Library supports namespace: " + qname[0] |
| + ", but no tag was defined for name: " + qname[1]); |
| } |
| else |
| { |
| TextUnit unit; |
| if (this.currentUnit() instanceof TextUnit) |
| { |
| unit = (TextUnit) this.currentUnit(); |
| } |
| else |
| { |
| unit = new TextUnit(this.alias, this.nextTagId(), |
| faceletsProcessingInstructions.isEscapeInlineText(), |
| faceletsProcessingInstructions.isCompressSpaces()); |
| this.startUnit(unit); |
| } |
| |
| if (this.compiler.isDevelopmentProjectStage()) |
| { |
| String qName = null; |
| boolean isPrefixed = false; |
| TagAttribute jsfc = t.getAttributes().get("jsfc"); |
| if (jsfc != null) |
| { |
| qName = jsfc.getValue(); |
| if (jsfc.getValue().indexOf(':') > 0) |
| { |
| isPrefixed = true; |
| } |
| } |
| else if (t.getQName().indexOf(':') > 0 ) |
| { |
| qName = t.getQName(); |
| isPrefixed = true; |
| } |
| if (isPrefixed) |
| { |
| unit.addMessage(FacesMessage.SEVERITY_WARN, |
| "Warning: The page "+alias+" declares namespace "+qname[0]+ |
| " and uses the tag " + qName + " , but no TagLibrary associated to namespace.", |
| "Warning: The page "+alias+" declares namespace "+qname[0]+ |
| " and uses the tag " + qName + " , but no TagLibrary associated to namespace. "+ |
| "Please check the namespace name and if it is correct, it is probably that your " + |
| "library .taglib.xml cannot be found on the current classpath, or if you are " + |
| "referencing a composite component library check your library folder match with the " + |
| "namespace and can be located by the installed ResourceHandler."); |
| } |
| } |
| |
| unit.startTag(t); |
| } |
| } |
| |
| public void popTag() |
| { |
| |
| if (this.finished) |
| { |
| return; |
| } |
| |
| CompilationUnit unit = this.currentUnit(); |
| |
| if (unit instanceof TextUnit) |
| { |
| TextUnit t = (TextUnit) unit; |
| if (t.isClosed()) |
| { |
| this.finishUnit(); |
| } |
| else |
| { |
| t.endTag(); |
| return; |
| } |
| } |
| |
| unit = this.currentUnit(); |
| if (unit instanceof TagUnit) |
| { |
| TagUnit t = (TagUnit) unit; |
| if (t instanceof TrimmedTagUnit) |
| { |
| this.finished = true; |
| return; |
| } |
| } |
| else if (unit instanceof CompositeComponentUnit) |
| { |
| this.finished = true; |
| return; |
| } |
| |
| this.finishUnit(); |
| } |
| |
| public void popNamespace(String ns) |
| { |
| this.namespaceManager.popNamespace(ns); |
| if (this.currentUnit() instanceof NamespaceUnit) |
| { |
| this.finishUnit(); |
| } |
| } |
| |
| public void pushNamespace(String prefix, String uri) |
| { |
| |
| if (log.isLoggable(Level.FINE)) |
| { |
| log.fine("Namespace Pushed " + prefix + ": " + uri); |
| } |
| |
| this.namespaceManager.pushNamespace(prefix, uri); |
| NamespaceUnit unit; |
| if (this.currentUnit() instanceof NamespaceUnit) |
| { |
| unit = (NamespaceUnit) this.currentUnit(); |
| } |
| else |
| { |
| unit = new NamespaceUnit(this.tagLibrary); |
| this.startUnit(unit); |
| } |
| unit.setNamespace(prefix, uri); |
| } |
| |
| public FaceletHandler createFaceletHandler() |
| { |
| return ((CompilationUnit) this.units.get(0)).createFaceletHandler(); |
| } |
| |
| private CompilationUnit currentUnit() |
| { |
| if (!this.units.isEmpty()) |
| { |
| return (CompilationUnit) this.units.peek(); |
| } |
| return null; |
| } |
| |
| private void finishUnit() |
| { |
| Object obj = this.units.pop(); |
| |
| if (log.isLoggable(Level.FINE)) |
| { |
| log.fine("Finished Unit: " + obj); |
| } |
| } |
| |
| private void startUnit(CompilationUnit unit) |
| { |
| |
| if (log.isLoggable(Level.FINE)) |
| { |
| log.fine("Starting Unit: " + unit + " and adding it to parent: " + this.currentUnit()); |
| } |
| |
| this.currentUnit().addChild(unit); |
| this.units.push(unit); |
| } |
| |
| private Tag trimAttributes(Tag tag) |
| { |
| Tag t = this.trimJSFCAttribute(tag); |
| t = this.trimNSAttributes(t); |
| return t; |
| } |
| |
| protected static boolean isRemove(String ns, String name) |
| { |
| return (UILibrary.NAMESPACE.equals(ns) || UILibrary.ALIAS_NAMESPACE.equals(ns)) && "remove".equals(name); |
| } |
| |
| protected static boolean isTrimmed(String ns, String name) |
| { |
| return (UILibrary.NAMESPACE.equals(ns) || UILibrary.ALIAS_NAMESPACE.equals(ns)) |
| && (CompositionHandler.NAME.equals(name) || ComponentRefHandler.NAME.equals(name)); |
| } |
| |
| protected static boolean isCompositeComponentInterface(String ns, String name) |
| { |
| return (CompositeLibrary.NAMESPACE.equals(ns) || CompositeLibrary.ALIAS_NAMESPACE.equals(ns)) |
| && InterfaceHandler.NAME.equals(name); |
| } |
| |
| protected static boolean isCompositeComponentImplementation(String ns, String name) |
| { |
| return (CompositeLibrary.NAMESPACE.equals(ns) || CompositeLibrary.ALIAS_NAMESPACE.equals(ns)) |
| && ImplementationHandler.NAME.equals(name); |
| } |
| |
| private String[] determineQName(Tag tag) |
| { |
| TagAttribute attr = tag.getAttributes().get("jsfc"); |
| if (attr != null) |
| { |
| if (log.isLoggable(Level.FINE)) |
| { |
| log.fine(attr + " JSF Facelet Compile Directive Found"); |
| } |
| String value = attr.getValue(); |
| String namespace; |
| String localName; |
| int c = value.indexOf(':'); |
| if (c == -1) |
| { |
| namespace = this.namespaceManager.getNamespace(""); |
| localName = value; |
| } |
| else |
| { |
| String prefix = value.substring(0, c); |
| namespace = this.namespaceManager.getNamespace(prefix); |
| if (namespace == null) |
| { |
| throw new TagAttributeException(tag, attr, "No Namespace matched for: " + prefix); |
| } |
| localName = value.substring(c + 1); |
| } |
| return new String[] { namespace, localName }; |
| } |
| else |
| { |
| return new String[] { tag.getNamespace(), tag.getLocalName() }; |
| } |
| } |
| |
| private Tag trimJSFCAttribute(Tag tag) |
| { |
| TagAttribute attr = tag.getAttributes().get("jsfc"); |
| if (attr != null) |
| { |
| TagAttribute[] oa = tag.getAttributes().getAll(); |
| TagAttribute[] na = new TagAttribute[oa.length - 1]; |
| int p = 0; |
| for (int i = 0; i < oa.length; i++) |
| { |
| if (!"jsfc".equals(oa[i].getLocalName())) |
| { |
| na[p++] = oa[i]; |
| } |
| } |
| return new Tag(tag, new TagAttributesImpl(na)); |
| } |
| return tag; |
| } |
| |
| private Tag trimNSAttributes(Tag tag) |
| { |
| TagAttribute[] attr = tag.getAttributes().getAll(); |
| int remove = 0; |
| for (int i = 0; i < attr.length; i++) |
| { |
| if (attr[i].getQName().startsWith("xmlns") && this.tagLibrary.containsNamespace(attr[i].getValue())) |
| { |
| remove |= 1 << i; |
| if (log.isLoggable(Level.FINE)) |
| { |
| log.fine(attr[i] + " Namespace Bound to TagLibrary"); |
| } |
| } |
| } |
| if (remove == 0) |
| { |
| return tag; |
| } |
| else |
| { |
| List<TagAttribute> attrList = new ArrayList<TagAttribute>(attr.length); |
| int p = 0; |
| for (int i = 0; i < attr.length; i++) |
| { |
| p = 1 << i; |
| if ((p & remove) == p) |
| { |
| continue; |
| } |
| attrList.add(attr[i]); |
| } |
| attr = attrList.toArray(new TagAttribute[attrList.size()]); |
| return new Tag(tag.getLocation(), tag.getNamespace(), tag.getLocalName(), tag.getQName(), |
| new TagAttributesImpl(attr)); |
| } |
| } |
| |
| /** |
| * |
| * @since 2.1.0 |
| * @return |
| */ |
| public FaceletsProcessingInstructions getFaceletsProcessingInstructions() |
| { |
| return faceletsProcessingInstructions; |
| } |
| } |
| |