Coverage Report - org.apache.myfaces.view.facelets.compiler.CompilationManager
 
Classes in this File Line Coverage Branch Coverage Complexity
CompilationManager
0%
0/214
0%
0/132
4.375
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.myfaces.view.facelets.compiler;
 20  
 
 21  
 import java.util.ArrayList;
 22  
 import java.util.List;
 23  
 import java.util.Stack;
 24  
 import java.util.logging.Level;
 25  
 import java.util.logging.Logger;
 26  
 
 27  
 import javax.faces.application.FacesMessage;
 28  
 import javax.faces.view.facelets.FaceletHandler;
 29  
 import javax.faces.view.facelets.Tag;
 30  
 import javax.faces.view.facelets.TagAttribute;
 31  
 import javax.faces.view.facelets.TagAttributeException;
 32  
 import javax.faces.view.facelets.TagDecorator;
 33  
 import javax.faces.view.facelets.TagException;
 34  
 
 35  
 import org.apache.myfaces.view.facelets.tag.TagAttributesImpl;
 36  
 import org.apache.myfaces.view.facelets.tag.TagLibrary;
 37  
 import org.apache.myfaces.view.facelets.tag.composite.CompositeLibrary;
 38  
 import org.apache.myfaces.view.facelets.tag.composite.ImplementationHandler;
 39  
 import org.apache.myfaces.view.facelets.tag.composite.InterfaceHandler;
 40  
 import org.apache.myfaces.view.facelets.tag.ui.ComponentRefHandler;
 41  
 import org.apache.myfaces.view.facelets.tag.ui.CompositionHandler;
 42  
 import org.apache.myfaces.view.facelets.tag.ui.UILibrary;
 43  
 
 44  
 /**
 45  
  * Compilation unit for managing the creation of a single FaceletHandler based on events from an XML parser.
 46  
  * 
 47  
  * @see org.apache.myfaces.view.facelets.compiler.Compiler
 48  
  * 
 49  
  * @author Jacob Hookom
 50  
  * @version $Id$
 51  
  */
 52  
 final class CompilationManager
 53  
 {
 54  
 
 55  
     //private final static Logger log = Logger.getLogger("facelets.compiler");
 56  0
     private final static Logger log = Logger.getLogger(CompilationManager.class.getName());
 57  
 
 58  
     private final Compiler compiler;
 59  
 
 60  
     private final TagLibrary tagLibrary;
 61  
 
 62  
     private final TagDecorator tagDecorator;
 63  
 
 64  
     private final NamespaceManager namespaceManager;
 65  
 
 66  
     private final Stack<CompilationUnit> units;
 67  
 
 68  
     private int tagId;
 69  
 
 70  
     private boolean finished;
 71  
 
 72  
     private final String alias;
 73  
     
 74  
     private CompilationUnit interfaceCompilationUnit;
 75  
     
 76  
     private final FaceletsProcessingInstructions faceletsProcessingInstructions;
 77  
 
 78  
     public CompilationManager(String alias, Compiler compiler, FaceletsProcessingInstructions instructions)
 79  0
     {
 80  
 
 81  
         // this is our alias
 82  0
         this.alias = alias;
 83  
 
 84  
         // grab compiler state
 85  0
         this.compiler = compiler;
 86  0
         this.tagDecorator = compiler.createTagDecorator();
 87  0
         this.tagLibrary = compiler.createTagLibrary();
 88  
 
 89  
         // namespace management
 90  0
         this.namespaceManager = new NamespaceManager();
 91  
 
 92  
         // tag uids
 93  0
         this.tagId = 0;
 94  
 
 95  
         // for composition use
 96  0
         this.finished = false;
 97  
 
 98  
         // our compilationunit stack
 99  0
         this.units = new Stack<CompilationUnit>();
 100  0
         this.units.push(new CompilationUnit());
 101  
         
 102  0
         this.interfaceCompilationUnit = null; 
 103  0
         this.faceletsProcessingInstructions = instructions;
 104  0
     }
 105  
 
 106  
     public void writeInstruction(String value)
 107  
     {
 108  0
         if (this.finished)
 109  
         {
 110  0
             return;
 111  
         }
 112  
 
 113  
         // don't carelessly add empty tags
 114  0
         if (value.length() == 0)
 115  
         {
 116  0
             return;
 117  
         }
 118  
 
 119  
         TextUnit unit;
 120  0
         if (this.currentUnit() instanceof TextUnit)
 121  
         {
 122  0
             unit = (TextUnit) this.currentUnit();
 123  
         }
 124  
         else
 125  
         {
 126  0
             unit = new TextUnit(this.alias, this.nextTagId(), 
 127  
                     faceletsProcessingInstructions.isEscapeInlineText(),
 128  
                     faceletsProcessingInstructions.isCompressSpaces());
 129  0
             this.startUnit(unit);
 130  
         }
 131  0
         unit.writeInstruction(value);
 132  0
     }
 133  
     
 134  
     public void writeDoctype(String name, String publicId, String systemId)
 135  
     {
 136  0
         if (this.finished)
 137  
         {
 138  0
             return;
 139  
         }
 140  
 
 141  0
         DoctypeUnit unit = new DoctypeUnit(this.alias, this.nextTagId(),
 142  
             name, publicId, systemId, faceletsProcessingInstructions.isHtml5Doctype());
 143  0
         this.startUnit(unit);
 144  0
     }
 145  
 
 146  
     public void writeText(String value)
 147  
     {
 148  
 
 149  0
         if (this.finished)
 150  
         {
 151  0
             return;
 152  
         }
 153  
 
 154  
         // don't carelessly add empty tags
 155  0
         if (value.length() == 0)
 156  
         {
 157  0
             return;
 158  
         }
 159  
 
 160  
         TextUnit unit;
 161  0
         if (this.currentUnit() instanceof TextUnit)
 162  
         {
 163  0
             unit = (TextUnit) this.currentUnit();
 164  
         }
 165  
         else
 166  
         {
 167  0
             unit = new TextUnit(this.alias, this.nextTagId(), 
 168  
                     faceletsProcessingInstructions.isEscapeInlineText(),
 169  
                     faceletsProcessingInstructions.isCompressSpaces());
 170  0
             this.startUnit(unit);
 171  
         }
 172  0
         unit.write(value);
 173  0
     }
 174  
 
 175  
     public void writeComment(String text)
 176  
     {
 177  0
         if (this.compiler.isTrimmingComments())
 178  
         {
 179  0
             return;
 180  
         }
 181  
 
 182  0
         if (this.finished)
 183  
         {
 184  0
             return;
 185  
         }
 186  
 
 187  
         // don't carelessly add empty tags
 188  0
         if (text.length() == 0)
 189  
         {
 190  0
             return;
 191  
         }
 192  
 
 193  
         TextUnit unit;
 194  0
         if (this.currentUnit() instanceof TextUnit)
 195  
         {
 196  0
             unit = (TextUnit) this.currentUnit();
 197  
         }
 198  
         else
 199  
         {
 200  0
             unit = new TextUnit(this.alias, this.nextTagId(), 
 201  
                     faceletsProcessingInstructions.isEscapeInlineText(),
 202  
                     faceletsProcessingInstructions.isCompressSpaces());
 203  0
             this.startUnit(unit);
 204  
         }
 205  
 
 206  0
         unit.writeComment(text);
 207  0
     }
 208  
 
 209  
     public void writeWhitespace(String text)
 210  
     {
 211  0
         if (!this.compiler.isTrimmingWhitespace())
 212  
         {
 213  0
             this.writeText(text);
 214  
         }
 215  0
     }
 216  
 
 217  
     private String nextTagId()
 218  
     {
 219  0
         return Integer.toHexString(Math.abs(this.alias.hashCode() ^ 13 * this.tagId++));
 220  
     }
 221  
 
 222  
     public void pushTag(Tag orig)
 223  
     {
 224  
 
 225  0
         if (this.finished)
 226  
         {
 227  0
             return;
 228  
         }
 229  
 
 230  0
         if (log.isLoggable(Level.FINE))
 231  
         {
 232  0
             log.fine("Tag Pushed: " + orig);
 233  
         }
 234  
 
 235  0
         Tag t = this.tagDecorator.decorate(orig);
 236  0
         String[] qname = this.determineQName(t);
 237  0
         t = this.trimAttributes(t);
 238  
 
 239  0
         if (isTrimmed(qname[0], qname[1]))
 240  
         {
 241  0
             log.fine("Composition Found, Popping Parent Tags");
 242  0
             this.units.clear();
 243  0
             NamespaceUnit nsUnit = this.namespaceManager.toNamespaceUnit(this.tagLibrary);
 244  0
             this.units.push(nsUnit);
 245  0
             this.startUnit(new TrimmedTagUnit(this.tagLibrary, qname[0], qname[1], t, this.nextTagId()));
 246  0
             log.fine("New Namespace and [Trimmed] TagUnit pushed");
 247  0
         }
 248  0
         else if (isRemove(qname[0], qname[1]))
 249  
         {
 250  0
             this.units.push(new RemoveUnit());
 251  
         }
 252  0
         else if (isCompositeComponentInterface(qname[0], qname[1]))
 253  
         {
 254  
             // Here we have two cases when we found a <composite:interface> tag:
 255  
             //
 256  
             // -  If a page has a <composite:interface> tag and a <composite:implementation> tag.
 257  
             //   In this case, we need to trim all tags outside this two tags, otherwise these
 258  
             //   unwanted tags will be added when the composite component is applied.
 259  
             //   Unfortunately, this is the only point we can do it, because after the compiler,
 260  
             //   html tags are wrapped on facelets UIInstruction or UIText components as "list",
 261  
             //   losing the original structure required to trim.
 262  
             //
 263  
             // -  If a page has a <composite:interface> tag and not a <composite:implementation> tag.
 264  
             //   In this case, it is not necessary to trim, because we use the facelet only to create
 265  
             //   metadata and the component tree created is not used (see
 266  
             //   ViewDeclarationLanguage.getComponentMetadata() ). On InterfaceHandler, instead
 267  
             //   there is some code that found the right component in the temporal tree to add the
 268  
             //   generated BeanInfo, which it is retrieved later.
 269  
             //
 270  
             // After use Template Client API for composite components, it was found the need to
 271  
             // gather metadata information from 
 272  0
             log.fine("Composite Component Interface Found, saving unit");
 273  0
             CompositeComponentUnit compositeRootCompilationUnit = new CompositeComponentUnit();
 274  0
             this.startUnit(compositeRootCompilationUnit);
 275  0
             interfaceCompilationUnit = new TagUnit(this.tagLibrary, qname[0], qname[1], t, this.nextTagId());
 276  0
             this.startUnit(interfaceCompilationUnit);
 277  0
         }        
 278  0
         else if (isCompositeComponentImplementation(qname[0], qname[1]))
 279  
         {
 280  0
             log.fine("Composite component Found, Popping Parent Tags");
 281  0
             this.units.clear();
 282  0
             NamespaceUnit nsUnit = this.namespaceManager.toNamespaceUnit(this.tagLibrary);
 283  0
             this.units.push(nsUnit);
 284  0
             CompositeComponentUnit compositeRootCompilationUnit = new CompositeComponentUnit();
 285  0
             this.startUnit(compositeRootCompilationUnit);
 286  0
             if (interfaceCompilationUnit != null)
 287  
             {
 288  0
                 this.currentUnit().addChild(interfaceCompilationUnit);
 289  0
                 interfaceCompilationUnit = null;
 290  
             }
 291  0
             this.startUnit(new TrimmedTagUnit(this.tagLibrary, qname[0], qname[1], t, this.nextTagId()));
 292  0
             log.fine("New Namespace and TagUnit pushed");
 293  0
         }        
 294  0
         else if (this.tagLibrary.containsTagHandler(qname[0], qname[1]))
 295  
         {
 296  0
             this.startUnit(new TagUnit(this.tagLibrary, qname[0], qname[1], t, this.nextTagId()));
 297  
         }
 298  0
         else if (this.tagLibrary.containsNamespace(qname[0]))
 299  
         {
 300  0
             throw new TagException(orig, "Tag Library supports namespace: " + qname[0]
 301  
                     + ", but no tag was defined for name: " + qname[1]);
 302  
         }
 303  
         else
 304  
         {
 305  
             TextUnit unit;
 306  0
             if (this.currentUnit() instanceof TextUnit)
 307  
             {
 308  0
                 unit = (TextUnit) this.currentUnit();
 309  
             }
 310  
             else
 311  
             {
 312  0
                 unit = new TextUnit(this.alias, this.nextTagId(),
 313  
                         faceletsProcessingInstructions.isEscapeInlineText(),
 314  
                         faceletsProcessingInstructions.isCompressSpaces());
 315  0
                 this.startUnit(unit);
 316  
             }
 317  
             
 318  0
             if (this.compiler.isDevelopmentProjectStage())
 319  
             {
 320  0
                 String qName = null;
 321  0
                 boolean isPrefixed = false;
 322  0
                 TagAttribute jsfc = t.getAttributes().get("jsfc");
 323  0
                 if (jsfc != null)
 324  
                 {
 325  0
                     qName = jsfc.getValue();
 326  0
                     if (jsfc.getValue().indexOf(':') > 0)
 327  
                     {
 328  0
                         isPrefixed = true;
 329  
                     }
 330  
                 }
 331  0
                 else if (t.getQName().indexOf(':') > 0 )
 332  
                 {
 333  0
                     qName = t.getQName();
 334  0
                     isPrefixed = true;
 335  
                 }
 336  0
                 if (isPrefixed)
 337  
                 {
 338  0
                     unit.addMessage(FacesMessage.SEVERITY_WARN,
 339  
                             "Warning: The page "+alias+" declares namespace "+qname[0]+ 
 340  
                             " and uses the tag " + qName + " , but no TagLibrary associated to namespace.", 
 341  
                             "Warning: The page "+alias+" declares namespace "+qname[0]+ 
 342  
                             " and uses the tag " + qName + " , but no TagLibrary associated to namespace. "+
 343  
                             "Please check the namespace name and if it is correct, it is probably that your " +
 344  
                             "library .taglib.xml cannot be found on the current classpath, or if you are " +
 345  
                             "referencing a composite component library check your library folder match with the " +
 346  
                             "namespace and can be located by the installed ResourceHandler.");
 347  
                 }
 348  
             }
 349  
             
 350  0
             unit.startTag(t);
 351  
         }
 352  0
     }
 353  
 
 354  
     public void popTag()
 355  
     {
 356  
 
 357  0
         if (this.finished)
 358  
         {
 359  0
             return;
 360  
         }
 361  
 
 362  0
         CompilationUnit unit = this.currentUnit();
 363  
 
 364  0
         if (unit instanceof TextUnit)
 365  
         {
 366  0
             TextUnit t = (TextUnit) unit;
 367  0
             if (t.isClosed())
 368  
             {
 369  0
                 this.finishUnit();
 370  
             }
 371  
             else
 372  
             {
 373  0
                 t.endTag();
 374  0
                 return;
 375  
             }
 376  
         }
 377  
 
 378  0
         unit = this.currentUnit();
 379  0
         if (unit instanceof TagUnit)
 380  
         {
 381  0
             TagUnit t = (TagUnit) unit;
 382  0
             if (t instanceof TrimmedTagUnit)
 383  
             {
 384  0
                 this.finished = true;
 385  0
                 return;
 386  
             }
 387  0
         }
 388  0
         else if (unit instanceof CompositeComponentUnit)
 389  
         {
 390  0
             this.finished = true;
 391  0
             return;
 392  
         }
 393  
 
 394  0
         this.finishUnit();
 395  0
     }
 396  
 
 397  
     public void popNamespace(String ns)
 398  
     {
 399  0
         this.namespaceManager.popNamespace(ns);
 400  0
         if (this.currentUnit() instanceof NamespaceUnit)
 401  
         {
 402  0
             this.finishUnit();
 403  
         }
 404  0
     }
 405  
 
 406  
     public void pushNamespace(String prefix, String uri)
 407  
     {
 408  
 
 409  0
         if (log.isLoggable(Level.FINE))
 410  
         {
 411  0
             log.fine("Namespace Pushed " + prefix + ": " + uri);
 412  
         }
 413  
 
 414  0
         this.namespaceManager.pushNamespace(prefix, uri);
 415  
         NamespaceUnit unit;
 416  0
         if (this.currentUnit() instanceof NamespaceUnit)
 417  
         {
 418  0
             unit = (NamespaceUnit) this.currentUnit();
 419  
         }
 420  
         else
 421  
         {
 422  0
             unit = new NamespaceUnit(this.tagLibrary);
 423  0
             this.startUnit(unit);
 424  
         }
 425  0
         unit.setNamespace(prefix, uri);
 426  0
     }
 427  
 
 428  
     public FaceletHandler createFaceletHandler()
 429  
     {
 430  0
         return ((CompilationUnit) this.units.get(0)).createFaceletHandler();
 431  
     }
 432  
 
 433  
     private CompilationUnit currentUnit()
 434  
     {
 435  0
         if (!this.units.isEmpty())
 436  
         {
 437  0
             return (CompilationUnit) this.units.peek();
 438  
         }
 439  0
         return null;
 440  
     }
 441  
 
 442  
     private void finishUnit()
 443  
     {
 444  0
         Object obj = this.units.pop();
 445  
 
 446  0
         if (log.isLoggable(Level.FINE))
 447  
         {
 448  0
             log.fine("Finished Unit: " + obj);
 449  
         }
 450  0
     }
 451  
 
 452  
     private void startUnit(CompilationUnit unit)
 453  
     {
 454  
 
 455  0
         if (log.isLoggable(Level.FINE))
 456  
         {
 457  0
             log.fine("Starting Unit: " + unit + " and adding it to parent: " + this.currentUnit());
 458  
         }
 459  
 
 460  0
         this.currentUnit().addChild(unit);
 461  0
         this.units.push(unit);
 462  0
     }
 463  
 
 464  
     private Tag trimAttributes(Tag tag)
 465  
     {
 466  0
         Tag t = this.trimJSFCAttribute(tag);
 467  0
         t = this.trimNSAttributes(t);
 468  0
         return t;
 469  
     }
 470  
 
 471  
     protected static boolean isRemove(String ns, String name)
 472  
     {
 473  0
         return (UILibrary.NAMESPACE.equals(ns) || UILibrary.ALIAS_NAMESPACE.equals(ns)) && "remove".equals(name);
 474  
     }
 475  
 
 476  
     protected static boolean isTrimmed(String ns, String name)
 477  
     {
 478  0
         return (UILibrary.NAMESPACE.equals(ns) || UILibrary.ALIAS_NAMESPACE.equals(ns))
 479  
                 && (CompositionHandler.NAME.equals(name) || ComponentRefHandler.NAME.equals(name));
 480  
     }
 481  
     
 482  
     protected static boolean isCompositeComponentInterface(String ns, String name)
 483  
     {
 484  0
         return (CompositeLibrary.NAMESPACE.equals(ns) || CompositeLibrary.ALIAS_NAMESPACE.equals(ns))
 485  
             && InterfaceHandler.NAME.equals(name);
 486  
     }
 487  
 
 488  
     protected static boolean isCompositeComponentImplementation(String ns, String name)
 489  
     {
 490  0
         return (CompositeLibrary.NAMESPACE.equals(ns) || CompositeLibrary.ALIAS_NAMESPACE.equals(ns))
 491  
             && ImplementationHandler.NAME.equals(name);
 492  
     }
 493  
 
 494  
     private String[] determineQName(Tag tag)
 495  
     {
 496  0
         TagAttribute attr = tag.getAttributes().get("jsfc");
 497  0
         if (attr != null)
 498  
         {
 499  0
             if (log.isLoggable(Level.FINE))
 500  
             {
 501  0
                 log.fine(attr + " JSF Facelet Compile Directive Found");
 502  
             }
 503  0
             String value = attr.getValue();
 504  
             String namespace;
 505  
             String localName;
 506  0
             int c = value.indexOf(':');
 507  0
             if (c == -1)
 508  
             {
 509  0
                 namespace = this.namespaceManager.getNamespace("");
 510  0
                 localName = value;
 511  
             }
 512  
             else
 513  
             {
 514  0
                 String prefix = value.substring(0, c);
 515  0
                 namespace = this.namespaceManager.getNamespace(prefix);
 516  0
                 if (namespace == null)
 517  
                 {
 518  0
                     throw new TagAttributeException(tag, attr, "No Namespace matched for: " + prefix);
 519  
                 }
 520  0
                 localName = value.substring(c + 1);
 521  
             }
 522  0
             return new String[] { namespace, localName };
 523  
         }
 524  
         else
 525  
         {
 526  0
             return new String[] { tag.getNamespace(), tag.getLocalName() };
 527  
         }
 528  
     }
 529  
 
 530  
     private Tag trimJSFCAttribute(Tag tag)
 531  
     {
 532  0
         TagAttribute attr = tag.getAttributes().get("jsfc");
 533  0
         if (attr != null)
 534  
         {
 535  0
             TagAttribute[] oa = tag.getAttributes().getAll();
 536  0
             TagAttribute[] na = new TagAttribute[oa.length - 1];
 537  0
             int p = 0;
 538  0
             for (int i = 0; i < oa.length; i++)
 539  
             {
 540  0
                 if (!"jsfc".equals(oa[i].getLocalName()))
 541  
                 {
 542  0
                     na[p++] = oa[i];
 543  
                 }
 544  
             }
 545  0
             return new Tag(tag, new TagAttributesImpl(na));
 546  
         }
 547  0
         return tag;
 548  
     }
 549  
 
 550  
     private Tag trimNSAttributes(Tag tag)
 551  
     {
 552  0
         TagAttribute[] attr = tag.getAttributes().getAll();
 553  0
         int remove = 0;
 554  0
         for (int i = 0; i < attr.length; i++)
 555  
         {
 556  0
             if (attr[i].getQName().startsWith("xmlns") && this.tagLibrary.containsNamespace(attr[i].getValue()))
 557  
             {
 558  0
                 remove |= 1 << i;
 559  0
                 if (log.isLoggable(Level.FINE))
 560  
                 {
 561  0
                     log.fine(attr[i] + " Namespace Bound to TagLibrary");
 562  
                 }
 563  
             }
 564  
         }
 565  0
         if (remove == 0)
 566  
         {
 567  0
             return tag;
 568  
         }
 569  
         else
 570  
         {
 571  0
             List<TagAttribute> attrList = new ArrayList<TagAttribute>(attr.length);
 572  0
             int p = 0;
 573  0
             for (int i = 0; i < attr.length; i++)
 574  
             {
 575  0
                 p = 1 << i;
 576  0
                 if ((p & remove) == p)
 577  
                 {
 578  0
                     continue;
 579  
                 }
 580  0
                 attrList.add(attr[i]);
 581  
             }
 582  0
             attr = attrList.toArray(new TagAttribute[attrList.size()]);
 583  0
             return new Tag(tag.getLocation(), tag.getNamespace(), tag.getLocalName(), tag.getQName(),
 584  
                            new TagAttributesImpl(attr));
 585  
         }
 586  
     }
 587  
 
 588  
     /**
 589  
      * 
 590  
      * @since 2.1.0
 591  
      * @return
 592  
      */
 593  
     public FaceletsProcessingInstructions getFaceletsProcessingInstructions()
 594  
     {
 595  0
         return faceletsProcessingInstructions;
 596  
     }
 597  
 }
 598