Package railo.transformer.cfml.tag

Source Code of railo.transformer.cfml.tag.CFMLTransformer

package railo.transformer.cfml.tag;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import railo.commons.io.res.util.ResourceUtil;
import railo.commons.lang.StringUtil;
import railo.commons.lang.types.RefBoolean;
import railo.commons.lang.types.RefBooleanImpl;
import railo.runtime.Info;
import railo.runtime.SourceFile;
import railo.runtime.config.ConfigImpl;
import railo.runtime.exp.ApplicationException;
import railo.runtime.exp.PageExceptionImpl;
import railo.runtime.exp.TemplateException;
import railo.runtime.op.Caster;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.util.KeyConstants;
import railo.runtime.type.util.ListUtil;
import railo.transformer.bytecode.Body;
import railo.transformer.bytecode.BodyBase;
import railo.transformer.bytecode.Page;
import railo.transformer.bytecode.Position;
import railo.transformer.bytecode.cast.CastOther;
import railo.transformer.bytecode.expression.Expression;
import railo.transformer.bytecode.expression.var.NullExpression;
import railo.transformer.bytecode.literal.LitString;
import railo.transformer.bytecode.statement.PrintOut;
import railo.transformer.bytecode.statement.StatementBase;
import railo.transformer.bytecode.statement.tag.Attribute;
import railo.transformer.bytecode.statement.tag.Tag;
import railo.transformer.bytecode.util.ASMUtil;
import railo.transformer.cfml.ExprTransformer;
import railo.transformer.cfml.attributes.AttributeEvaluatorException;
import railo.transformer.cfml.evaluator.EvaluatorException;
import railo.transformer.cfml.evaluator.impl.ProcessingDirectiveException;
import railo.transformer.cfml.expression.SimpleExprTransformer;
import railo.transformer.cfml.script.AbstrCFMLScriptTransformer.ComponentTemplateException;
import railo.transformer.cfml.script.CFMLScriptTransformer;
import railo.transformer.library.function.FunctionLib;
import railo.transformer.library.tag.CustomTagLib;
import railo.transformer.library.tag.TagLib;
import railo.transformer.library.tag.TagLibException;
import railo.transformer.library.tag.TagLibFactory;
import railo.transformer.library.tag.TagLibTag;
import railo.transformer.library.tag.TagLibTagAttr;
import railo.transformer.util.CFMLString;


/**
* Die Klasse CFMLTransformer ist das Herzstck des ᅵbersetzungsprozess,
* es liest die bergebene CFML Datei ein und bersetzt diese in ein valid (CFXD) XML Dokument
* in der Form eines org.w3c.dom.Document Object,
* die dann als weitere Vorlage zum bersetzten in PHP dient.
* Der CFMLTransformer bersetzt nur die Tags die innerhalb einer CFML Seite vorkommen,
* nicht die Ausdrcke die innerhalb von Attributen und dem Body eines Tag vorkommen k￶nnen,
* fr dies ist der ExprTransformer zust¦ndig,
* der in der jeweiligen Tag Library definiert ist.
* Der CFMLTransformer kann zwar durch seine Grammatik,
* Tags erkennen aber nicht validieren.
* Erst mithilfe der im zugeteilten Tag Libraries kann er vergleichen ob ein Tag nur
* ein normaler HTML Tag ist, das er einfach als literale Zeichenkette aufnimmt,
* oder ob es sich um einen Tag handelt der eine konkrete Anweisung implementiert.
* Die Tag Library definiert alle in CFML vorhanden Tags,
* deren individuelle Grammatik und deren Aufbau und Verhalten.

* <pre>
    Parser Grammatik nach EBNF (Extended Backus-Naur Form)
   
    transform  = {body}
    body    = [comment] ("</" | "<" tag body | literal body);
    comment    = "<!---" {?-"--->"} "--->";
    literal    = ("<" | {?-"#"-"<"} "<" | {"#" expression "#"} "<" ) | ({?-"<"} "<")
          (* Welcher Teil der "oder" Bedingung ausgefhrt wird, ist abh¦ngig was die Tag-Lib vorgibt,
             dass Expression geparst werden sollen oder nicht. *)
    tag    = name-space identifier spaces attributes ("/>" | ">" [body "</" identifier spaces ">"]);
          (* Ob dem Tag ein Body und ein End-Tag folgt ist abh¦ngig von Definition des body-content in Tag-Lib, gleices gilt fr appendix *)
    name-space  = < tagLib[].getNameSpaceAndSeperator() >;
                (* Vergleicht Zeichen mit den Namespacedefinitionen der Tag Libraries. *)
    attributes  = ({spaces attribute} "/>" | {spaces attribute} ">") | attribute-value;
          (* Welcher Teil der "oder" Bedingung ausgefhrt wird, ist abh¦ngig von der Tag Attribute Definition in der Tag Lib. *)
    attribute  = attribute-name  spaces "=" spaces attribute-value;
    attribute-name  = ("expression"|'expression'|expression) | identifier;
                (* Ruft identifier oder den Expression Transformer auf je nach Attribute Definition in der Tag Lib. *)
    attribute-value  = expression;
    identifier       = (letter | "_") {letter | "_"|digit};
    letter      = "a".."z"|"A".."Z";
    digit      = "0".."9";
    expression      = <ExprTransfomer.expression()>; (* Ruft den Expression Transformer auf. *)
    spaces         = {space};
    space          = "\s"|"\t"|"\f"|"\t"|"\n";
   
    {"x"}= 0 bis n mal "x"
    ["x"]= 0 bis 1 mal "x"
    ("x" | "y")"z" = "xz" oder "yz"
</pre>
*
*
*/
public final class CFMLTransformer {
 
    public static short TAG_LIB_GLOBAL=0;
    public static short TAG_LIB_PAGE=1;
   
   
 
  /**
   * Startmethode zum transfomieren einer CFML Datei.
   * <br />
   * EBNF:<br />
   * <code>{body}</code>
   * @param config
   * @param sf CFML File
   * @param tlibs Tag Library Deskriptoren, nach denen innerhalb der CFML Datei geprft werden soll.
   * @param flibs Function Library Deskriptoren, nach denen innerhalb der Expressions der CFML Datei geprft werden soll.
   * @return ᅵbersetztes CFXD Dokument Element.
   * @throws TemplateException
   * @throws IOException
   */
  public Page transform(ConfigImpl config,SourceFile sf, TagLib[] tlibs, FunctionLib[] flibs) throws TemplateException, IOException  {
    Page p;
    CFMLString cfml;
   
    boolean writeLog=config.getExecutionLogEnabled();
    String charset=config.getTemplateCharset();
    boolean dotUpper = config.getDotNotationUpperCase();
   
   
    while(true){
      try {
        cfml=new CFMLString(sf,charset,writeLog);
        p = transform(config,cfml,tlibs,flibs,sf.getResource().lastModified(),dotUpper);
        break;
      }
      catch(ProcessingDirectiveException pde) {
        if(pde.getWriteLog()!=null)writeLog=pde.getWriteLog().booleanValue();
        if(pde.getDotNotationUpperCase()!=null)dotUpper=pde.getDotNotationUpperCase().booleanValue();
        if(!StringUtil.isEmpty(pde.getCharset()))charset=pde.getCharset();
      }
    }
   
    // if cfc has no component tag or is script without cfscript
    if(p.isPage() && ResourceUtil.getExtension(sf.getResource(),"").equalsIgnoreCase(config.getCFCExtension())){
      cfml.setPos(0);
      TagLibTag tlt;
      CFMLString original = cfml;
     
      // try inside a cfscript
      tlt = CFMLTransformer.getTLT(original,"script");
      String text="<"+tlt.getFullName()+">"+original.getText()+"</"+tlt.getFullName()+">";
      cfml=new CFMLString(text,charset,writeLog,sf);
     
      try {
        while(true){
          if(cfml==null){
            cfml=new CFMLString(sf,charset,writeLog);
            text="<"+tlt.getFullName()+">"+cfml.getText()+"</"+tlt.getFullName()+">";
            cfml=new CFMLString(text,charset,writeLog,sf);
          }
          try {
            p= transform(config,cfml,tlibs,flibs,sf.getResource().lastModified(),dotUpper);
            break;
          }
          catch(ProcessingDirectiveException pde) {
            if(pde.getWriteLog()!=null)writeLog=pde.getWriteLog().booleanValue();
            if(pde.getDotNotationUpperCase()!=null)dotUpper=pde.getDotNotationUpperCase().booleanValue();
            if(!StringUtil.isEmpty(pde.getCharset()))charset=pde.getCharset();
            cfml=null;
          }
        }
      }
      catch (ComponentTemplateException e) {
        throw e.getTemplateException();
      }
      catch (TemplateException e) {
        //print.printST(e);
      }
     
     
     
     
      // try inside a component
      if(p.isPage()){
        tlt = CFMLTransformer.getTLT(original,"component");
        text="<"+tlt.getFullName()+">"+original.getText()+"</"+tlt.getFullName()+">";
        cfml=new CFMLString(text,charset,writeLog,sf);
           
        while(true){
          if(cfml==null){
            cfml=new CFMLString(sf,charset,writeLog);
            text="<"+tlt.getFullName()+">"+cfml.getText()+"</"+tlt.getFullName()+">";
            cfml=new CFMLString(text,charset,writeLog,sf);
          }
          try {
            p= transform(config,cfml,tlibs,flibs,sf.getResource().lastModified(),dotUpper);
            break;
          }
          catch(ProcessingDirectiveException pde) {
            if(pde.getWriteLog()!=null)writeLog=pde.getWriteLog().booleanValue();
            if(pde.getDotNotationUpperCase()!=null)dotUpper=pde.getDotNotationUpperCase().booleanValue();
            if(!StringUtil.isEmpty(pde.getCharset()))charset=pde.getCharset();
            cfml=null;
          }
        }
      }
     
    }
   
   
    return p;
  }
 

  public static TagLibTag getTLT(CFMLString cfml,String name) throws TemplateException {
    TagLib tl;
    try {
      // this is already loaded, oherwise we where not here
      tl = TagLibFactory.loadFromSystem();
      return tl.getTag(name);
    }
    catch (TagLibException e) {
      throw new TemplateException(cfml,e);
    }
  }
 

  /**
   * Startmethode zum transfomieren einer CFMLString.
   * <br />
   * EBNF:<br />
   * <code>{body}</code>
   * @param config
   * @param cfml CFMLString
   * @param tlibs Tag Library Deskriptoren, nach denen innerhalb der CFML Datei geprft werden soll.
   * @param flibs Function Library Deskriptoren, nach denen innerhalb der Expressions der CFML Datei geprft werden soll.
   * @param sourceLastModified
   * @return ᅵbersetztes CFXD Dokument Element.
   * @throws TemplateException
   */
  public Page transform(ConfigImpl config,CFMLString cfml,TagLib[] tlibs,FunctionLib[] flibs, long sourceLastModified, Boolean dotNotationUpperCase) throws TemplateException {
   
    TagLib[][] _tlibs=new TagLib[][]{null,new TagLib[0]};
    _tlibs[TAG_LIB_GLOBAL]=tlibs;
    // reset page tlds
    if(_tlibs[TAG_LIB_PAGE].length>0) {
      _tlibs[TAG_LIB_PAGE]=new TagLib[0];
    }
   
   
   

    SourceFile source=cfml.getSourceFile();
   
    Page page=new Page(config,source.getPhyscalFile(),source.getFullClassName(),Info.getFullVersionInfo(),sourceLastModified,cfml.getWriteLog(),config.getSupressWSBeforeArg());
    TagData data = new TagData(_tlibs,flibs,config.getCoreTagLib().getScriptTags(),cfml,dotNotationUpperCase,page);
   
    //Body body=page;
    try {
      do {
       
        body(data,page,false,null);
       
        if(data.cfml.isAfterLast()) break;
        if(data.cfml.forwardIfCurrent("</")){
          int pos = data.cfml.getPos();
          TagLib tagLib=nameSpace(data);
          if(tagLib==null){
            page.addPrintOut("</", null,null);
          }
          else {
            String name = identifier(data.cfml,true);
            if(tagLib.getIgnoreUnknowTags()) {
              TagLibTag tlt = tagLib.getTag(name);
              if(tlt==null) {
                data.cfml.setPos(pos);
                page.addPrintOut("</", null,null);
              }
            }
            else throw new TemplateException(cfml,"no matching start tag for end tag ["+tagLib.getNameSpaceAndSeparator()+name+"]");
     
          }
        }
        else
          throw new TemplateException(cfml,"Error while transforming CFML File");
      }while(true);
     
      // call-back of evaluators
      data.ep.run();
     
      return page;
    }
    catch(TemplateException e) {
        data.ep.clear();
        throw e;
    }
  }

  /**
   * Liest den Body eines Tag ein. Kommentare, Tags und Literale inkl. Expressions.
   * <br />
   * EBNF:<br />
   * <code>[comment] ("</" | "<" tag body | literal body);</code>
   * @param body CFXD Body Element dem der Inhalt zugeteilt werden soll.
   * @param parseExpression Definiert ob Expressions innerhalb von Literalen bersetzt werden sollen oder nicht.
   * @param transformer Expression Transfomer zum bersetzten von Expression.
   * @throws TemplateException
   */
  private void body(TagData data,Body body, boolean parseExpression, ExprTransformer transformer) throws TemplateException {
    boolean parseLiteral=true;
   
  // Comment
        comment(data.cfml,false);
  // Tag
    // is Tag Beginning
    if(data.cfml.isCurrent('<'))  {
      // return if end tag and inside tag
      if(data.cfml.isNext('/'))  {
          //railo.print.ln("early return");
        return;
      }
      parseLiteral=!tag(data,body,parseExpression);
    }
    // no Tag
    if(parseLiteral) {
      literal(data,body, parseExpression, transformer);
    }
    // not at the end
    if(data.cfml.isValidIndex())
      body(data,body,parseExpression, transformer);
  }
 
  /**
   * Liest einen Kommentar ein, Kommentare werden nicht in die CFXD bertragen sondern verworfen.
   * Komentare k￶nnen auch Kommentare enthalten.
   * <br />
   * EBNF:<br />
   * <code>"<!---" {?-"--->"} "--->";</code>
   * @throws TemplateException
   */

  public static void comment(CFMLString cfml,boolean removeSpace) throws TemplateException {
    if(!removeSpace) {
      comment(cfml);
    }
    else {
      cfml.removeSpace();
      if(comment(cfml))cfml.removeSpace();
    }
   
  }
 
  public static boolean comment(CFMLString cfml) throws TemplateException {
    if(!cfml.forwardIfCurrent("<!---"))
      return false;
   
    int start=cfml.getPos();
    short counter=1;
    while(true) {
      if(cfml.isAfterLast()) {
        cfml.setPos(start);
        throw new TemplateException(cfml,"no end comment found");
      }
      else if(cfml.forwardIfCurrent("<!---")) {
        counter++;
      }
      else if(cfml.forwardIfCurrent("--->")) {
        if(--counter==0) {
          comment(cfml);
          return true;
        }
      }
      else {
        cfml.next();
      }
    }
  }



  /**
   * Liest Literale Zeichenketten ein die sich innerhalb und auserhalb von tgas befinden,
   * beim Einlesen wird unterschieden ob Expression geparsst werden mssen oder nicht,
   * dies ist abh¦ngig, von der Definition des Tag in dem man sich allenfalls befindet, innerhalb der TLD.
   * @param parent bergeordnetes Element.
   * @param parseExpression Definiert on Expressions geparset werden sollen oder nicht.
   * @param transformer Expression Transfomer zum bersetzen der Expressions innerhalb des Literals.
   * @throws TemplateException
   *
   * <br />
   * EBNF:<br />
   * <code>("<" | {?-"#"-"<"} "<" | {"#" expression "#"} "<" ) | ({?-"<"} "<")
      (* Welcher Teil der "oder" Bedingung ausgefhrt wird, ist abh¦ngig ob die Tag-Lib vorgibt,
       dass Expression geparst werden sollen oder nicht. *)</code>
   */
  private void literal(TagData data,Body parent,boolean parseExpression, ExprTransformer transformer) throws TemplateException {
    // with expression
    if(parseExpression) {
      if(data.cfml.isAfterLast())return;
            // data.cfml.getCurrent()
      StringBuffer text=new StringBuffer();
      int count=0;
      while(data.cfml.isValidIndex()) {
        count++;
        // #
        if(data.cfml.isCurrent('#')) {
          data.cfml.next();
          if(data.cfml.isCurrent('#')) {
            text.append('#');
          }
          else {
            if(text.length()>0)  {
              parent.addPrintOut(text.toString(),null,null);
              text=new StringBuffer();
            }
                        Position line = data.cfml.getPosition();
            PrintOut po;
            parent.addStatement(po=new PrintOut(transformer.transform(data.page,data.ep,data.flibs,data.scriptTags,data.cfml,data.settings),line,null));
            po.setEnd(data.cfml.getPosition());
           
            if(!data.cfml.isCurrent('#'))
              throw new TemplateException(data.cfml,"missing terminating [#] for expression");
          }
        }
        else if(data.cfml.isCurrent('<') && count>1) {
          break;
        }
        else
          text.append(data.cfml.getCurrent());
      data.cfml.next();
      }
      if(text.length()>0)parent.addPrintOut(text.toString(), null,null);
    }
    // no expression
    else {
      int start=data.cfml.getPos();
      data.cfml.next();
      int end=data.cfml.indexOfNext('<');
      String text;
      if(end==-1) {
        text=data.cfml.substring(start);
        data.cfml.setPos(data.cfml.length());
      }
      else {
        text=data.cfml.substring(start,end-start);
        data.cfml.setPos(end);
      }
      parent.addPrintOut(text, null,null);
     
    }
  }

  /**
   * Liest einen Tag ein, prft hierbei ob das Tag innerhalb einer der geladenen Tag-Lib existiert,
   * ansonsten wird ein Tag einfach als literal-string aufgenommen.
   * <br />
   * EBNF:<br />
   * <code>name-space identifier spaces attributes ("/>" | ">" [body "</" identifier spaces ">"]);(* Ob dem Tag ein Body und ein End-Tag folgt ist abh¦ngig von Definition des body-content in Tag-Lib, gleices gilt fr appendix *)</code>
   * @param parent bergeornetes Tag
   * @param parseExpression sollen Expresson innerhalb des Body geparste werden oder nicht.
   * @return Gibt zurck ob es sich um ein Tag as einer Tag-Lib handelte oder nicht.
   * @throws TemplateException
   */
  private boolean tag(TagData data,Body parent,boolean parseExpression) throws TemplateException {
      //railo.print.ln("--->"+data.cfml.getCurrent());
      boolean hasBody=false;
   
    Position line = data.cfml.getPosition();
    //int column=data.cfml.getColumn();
    int start=data.cfml.getPos();
    data.cfml.next();
   
    // read in namesapce of tag
    TagLib tagLib=nameSpace(data);
   
    // return if no matching tag lib
    if(tagLib==null)  {
      data.cfml.previous();
      return false;
    }
       
    // Get matching tag from tag lib
    String strNameNormal=identifier(data.cfml,false);
    if(strNameNormal==null) {
      data.cfml.setPos((data.cfml.getPos()-tagLib.getNameSpaceAndSeparator().length())-1);
      return false;
    }
   
    String strName=strNameNormal.toLowerCase();
    String appendix=null;
    TagLibTag tagLibTag=tagLib.getTag(strName);
   
    // get taglib
    if(tagLibTag==null)  {
      tagLibTag=tagLib.getAppendixTag(strName);
       if(tagLibTag==null) {
         if(tagLib.getIgnoreUnknowTags()){
           data.cfml.setPos(start);
           return false;
         }
         throw new TemplateException(data.cfml,"undefined tag ["+tagLib.getNameSpaceAndSeparator()+strName+"]");
       }
      appendix=StringUtil.removeStartingIgnoreCase(strNameNormal,tagLibTag.getName());
     }
   
    // CFXD Element
    Tag tag;
    try {
      tag = tagLibTag.getTag(line,data.cfml.getPosition());
    }
    catch (Exception e) {
      throw new TemplateException(data.cfml,e);
    }
    parent.addStatement(tag);
   
    // get tag from tag library
     if(appendix!=null)  {
      tag.setAppendix(appendix);
      tag.setFullname(tagLibTag.getFullName().concat(appendix));
     }
     else {
       tag.setFullname(tagLibTag.getFullName());    
     }
     if(tag.getFullname().equalsIgnoreCase("cfcomponent"))data.page.setIsComponent(true)// MUST to hardcoded, to better
     else if(tag.getFullname().equalsIgnoreCase("cfinterface"))data.page.setIsInterface(true)// MUST to hardcoded, to better
      
    tag.setTagLibTag(tagLibTag);
    comment(data.cfml,true);
   
    // Tag Translator Evaluator
    if(tagLibTag.hasTteClass())  {
      data.ep.add(tagLibTag,tag,data.flibs,data.cfml);
    }
   
    //get Attributes
    attributes(data,tagLibTag,tag);
   
    if(tagLibTag.hasAttributeEvaluator()) {
      try {
        tagLibTag=tagLibTag.getAttributeEvaluator().evaluate(tagLibTag,tag);
      } catch (AttributeEvaluatorException e) {
       
        throw new TemplateException(data.cfml, e);
      }
    }
   
   
   
    // End of begin Tag
//    TODO muss erlaubt sein
    if(data.cfml.forwardIfCurrent('>'))  {
      hasBody=tagLibTag.getHasBody();
    }
    else if(data.cfml.forwardIfCurrent('/','>')) {
      if(tagLibTag.getHasBody())tag.setBody(new BodyBase());
    }
    else {
      throw createTemplateException(data.cfml, "tag ["+tagLibTag.getFullName()+"] is not closed",tagLibTag);
    }
   

    // Body
    if(hasBody)  {

       
      // get Body
      if(tagLibTag.isTagDependent()) {
        // get TagDependentBodyTransformer
        TagDependentBodyTransformer tdbt=null;
        try {
          tdbt=tagLibTag.getBodyTransformer();
        } catch (TagLibException e) {
          throw new TemplateException(data.cfml,e);
        }
        if(tdbt==null) throw createTemplateException(data.cfml,"Tag dependent body Transformer is invalid for Tag ["+tagLibTag.getFullName()+"]",tagLibTag);
        tdbt.transform(data.page,this,data.ep,data.flibs,tag,tagLibTag,data.scriptTags,data.cfml,data.settings);
       
        //  get TagLib of end Tag
        if(!data.cfml.forwardIfCurrent("</")) {
          // MUST this is a patch, do a more proper implementation
          TemplateException te = new TemplateException(data.cfml,"invalid construct");
          if(tdbt instanceof CFMLScriptTransformer && ASMUtil.containsComponent(tag.getBody())) {
            throw new CFMLScriptTransformer.ComponentTemplateException(te);
          }
          throw te;
        }
       
        TagLib tagLibEnd=nameSpace(data);
        // same NameSpace
        if(!(tagLibEnd!=null && tagLibEnd.getNameSpaceAndSeparator().equals(tagLib.getNameSpaceAndSeparator())))
          throw new TemplateException(data.cfml,"invalid construct");
        // get end Tag
        String strNameEnd=identifier(data.cfml,true).toLowerCase();

        // not the same name Tag
        if(!strName.equals(strNameEnd)) {
          data.cfml.setPos(start);
          throw new TemplateException(data.cfml,"Start and End Tag has not the same Name ["+tagLib.getNameSpaceAndSeparator()+strName+"-"+tagLibEnd.getNameSpaceAndSeparator()+strNameEnd+"]");       
         }
         data.cfml.removeSpace();
         if(!data.cfml.forwardIfCurrent('>'))
           throw new TemplateException(data.cfml,"End Tag ["+tagLibEnd.getNameSpaceAndSeparator()+strNameEnd+"] not closed");
      }
      else {
        // get body of Tag
        BodyBase body=new BodyBase();
        body.setParent(tag);
        //tag.setBody(body);
          //parseExpression=(tagLibTag.getParseBody())?true:parseExpression;
        if(tagLibTag.getParseBody())parseExpression=true;
       
        while(true)  {
         
          // Load Expession Transformer from TagLib
          ExprTransformer transfomer=null;
          if(parseExpression) {
            try {
              transfomer = tagLibTag.getTagLib().getExprTransfomer();
            } catch (TagLibException e) {
              throw new TemplateException(data.cfml,e);
            }
          }
         

          // call body
         
            body(data,body,parseExpression,transfomer);
          
           
            // no End Tag
          if(data.cfml.isAfterLast()) {
             
            if(tagLibTag.isBodyReq()) {
                data.cfml.setPos(start);
              throw createTemplateException(data.cfml,"No matching end tag found for tag ["+tagLibTag.getFullName()+"]",tagLibTag);
            }
            body.moveStatmentsTo(parent);
            return executeEvaluator(data,tagLibTag, tag);
          }
         
          // Invalid Construct
          int posBeforeEndTag=data.cfml.getPos();
          if(!data.cfml.forwardIfCurrent('<','/'))
            throw createTemplateException(data.cfml,"Missing end tag for ["+tagLibTag.getFullName()+"]",tagLibTag);
         
          // get TagLib of end Tag
          int _start = data.cfml.getPos();
          TagLib tagLibEnd=nameSpace(data);
         
          // same NameSpace
          if(tagLibEnd!=null)  {
              String strNameEnd="";
              //railo.print.ln(data.cfml.getLine()+" - "+data.cfml.getColumn()+" - "+tagLibEnd.getNameSpaceAndSeperator()+".equals("+tagLib.getNameSpaceAndSeperator()+")");
              if(tagLibEnd.getNameSpaceAndSeparator().equals(tagLib.getNameSpaceAndSeparator())) {
                 
                // get end Tag
              strNameEnd=identifier(data.cfml,true).toLowerCase();
              // not the same name Tag
             
              // new part
              data.cfml.removeSpace();
              if(strName.equals(strNameEnd)) {
                  if(!data.cfml.forwardIfCurrent('>'))
                  throw new TemplateException(data.cfml,"End Tag ["+tagLibEnd.getNameSpaceAndSeparator()+strNameEnd+"] not closed");
                break;
              }
             
              }
              // new part
              if(tagLibTag.isBodyReq()) {
              TagLibTag endTag = tagLibEnd.getTag(strNameEnd);
              if(endTag!=null && !endTag.getHasBody())
                throw new TemplateException(data.cfml,
                    "End Tag ["+
                    tagLibEnd.getNameSpaceAndSeparator()+strNameEnd+"] is not allowed, for this tag only a Start Tag is allowed");
              data.cfml.setPos(start);
              if(tagLibEnd.getIgnoreUnknowTags() && (tagLibEnd.getTag(strNameEnd))==null){
                data.cfml.setPos(_start);
              }
              else throw new TemplateException(data.cfml,
                  "Start and End Tag has not the same Name ["+
                  tagLib.getNameSpaceAndSeparator()+strName+"-"+tagLibEnd.getNameSpaceAndSeparator()+strNameEnd+"]");
            }
              else {
              body.moveStatmentsTo(parent);
              data.cfml.setPos(posBeforeEndTag);
              return executeEvaluator(data,tagLibTag, tag);
              }
              /// new part 
          }
          body.addPrintOut("</",null,null);
         
        }
        tag.setBody(body);
       
      }
    }
    if(tag instanceof StatementBase)
      ((StatementBase)tag).setEnd(data.cfml.getPosition());
    // Tag Translator Evaluator
   
    return executeEvaluator(data,tagLibTag, tag);
       
  }
 
  private boolean executeEvaluator(TagData data,TagLibTag tagLibTag, Tag tag) throws TemplateException {
    if(tagLibTag.hasTteClass())  {
      try {
        TagLib lib=tagLibTag.getEvaluator().execute(data.config,tag,tagLibTag,data.flibs,data);
        if(lib!=null) {
          // set
          for(int i=0;i<data.tlibs[TAG_LIB_PAGE].length;i++) {
                    if(data.tlibs[TAG_LIB_PAGE][i].getNameSpaceAndSeparator().equalsIgnoreCase(lib.getNameSpaceAndSeparator())){
                      boolean extIsCustom=data.tlibs[TAG_LIB_PAGE][i] instanceof CustomTagLib;
                      boolean newIsCustom=lib instanceof CustomTagLib;
                      // TagLib + CustomTagLib (visa/versa)
                      if(extIsCustom){
                        ((CustomTagLib)data.tlibs[TAG_LIB_PAGE][i]).append(lib);
                        return true;
                      }
                      else if(newIsCustom){
                        ((CustomTagLib)lib).append(data.tlibs[TAG_LIB_PAGE][i]);
                        data.tlibs[TAG_LIB_PAGE][i]=lib;
                        return true;
                      }
                    }
                }
          // TODO make sure longer namespace ar checked firts to support subsets, same for core libs
          // insert
                TagLib[] newTlibs=new TagLib[data.tlibs[TAG_LIB_PAGE].length+1];
                    for(int i=0;i<data.tlibs[TAG_LIB_PAGE].length;i++) {
                        newTlibs[i]=data.tlibs[TAG_LIB_PAGE][i];
                    }
                    newTlibs[data.tlibs[TAG_LIB_PAGE].length]=lib;
                    data.tlibs[TAG_LIB_PAGE]=newTlibs;   
        }
      }
            catch (EvaluatorException e) {
                throw new TemplateException(data.cfml,e);
            }
    }
    return true;
  }
 
  /**
   * Vergleicht folgende Zeichen mit den Namespacedefinitionen der Tag Libraries,
   * gibt eine Tag-Lib zurck falls eine passt, ansonsten null.
   * <br />
   * EBNF:<br />
   * <code>< tagLib[].getNameSpaceAndSeperator() >(* Vergleicht Zeichen mit den Namespacedefinitionen der Tag Libraries. *) </code>
   * @return TagLib Passende Tag Lirary oder null.
   */
  private TagLib nameSpace(TagData data) {
    boolean hasTag=false;
    int start = data.cfml.getPos();
    TagLib tagLib=null;
   
    // loop over NameSpaces
    for(int i=1;i>=0;i--)  {
      for(int ii=0;ii<data.tlibs[i].length;ii++)  {
        tagLib= data.tlibs[i][ii];
        char[] c=tagLib.getNameSpaceAndSeperatorAsCharArray();
        // Loop over char of NameSpace and Sepearator
        hasTag=true;
        for(int y=0;y<c.length;y++)  {
          if(!(data.cfml.isValidIndex() && c[y]==data.cfml.getCurrentLower())) {
            //hasTag=true;
          //} else {
            hasTag=false;
            data.cfml.setPos(start);
            break;
          }
          data.cfml.next();
        }
        if(hasTag)return tagLib;//break;
      }
      //if(hasTag) return tagLib;
    }
    return null;
  }

  /**
   * Liest die Attribute eines Tags ein, dies Abh¦ngig von der Definition innerhalb der Tag-Lib.
   * Hierbei unterscheiden wir vier verschiedene Arten von Attributen:<br>
   * <ul>
   * <li>FIX: Definierte Attribute Fix, fr jedes Attribut ist definiert ob es required ist oder nicht (gleich wie JSP). </li>
   * <li>DYNAMIC: Die Attribute des Tag sind frei, keine Namen sind vorgegeben.
   * Es kann aber definiert sein wieviele Attribute maximal und minimal verwendetet werden drfen.</li>
   * <li>FULLDYNAMIC: Gleich wie DYNAMIC, jedoch kann der Name des Attribut auch ein dynamischer Wert sein (wie bei cfset).</li>
   * <li>NONAME: Ein Tag welches nur ein Attribut besitzt ohne Name, sondern einfach nur mit einem Attribut Wert</li>
   * </ul>
   * <br />
   * EBNF:<br />
   * <code>({spaces attribute} "/>" | {spaces attribute} ">") | attribute-value;(* Welcher Teil der "oder" Bedingung ausgefhrt wird, ist abh¦ngig von der Tag Attribute Definition in der Tag Lib. *)</code>
   * @param tag
   * @param parent
   * @throws TemplateException
   */
  public static void attributes(TagData data,TagLibTag tag, Tag parent) throws TemplateException {
    int type=tag.getAttributeType();
    int start = data.cfml.getPos();
  // Tag with attribute names
    iftype!=TagLibTag.ATTRIBUTE_TYPE_NONAME)  {
      try{
      int min=tag.getMin();
      int max=tag.getMax();
      int count=0;
      ArrayList<String> args=new ArrayList<String>();
      RefBoolean allowDefaultValue=new RefBooleanImpl(tag.getDefaultAttribute()!=null);
      while(data.cfml.isValidIndex())  {
        data.cfml.removeSpace();
        // if no more attributes break
        if(data.cfml.isCurrent('/') || data.cfml.isCurrent('>')) break;
       
        parent.addAttribute(attribute(data,tag,args,allowDefaultValue));
        count++;   
      }
           
      // set default values
      if(tag.hasDefaultValue()) {
          Map<String, TagLibTagAttr> hash = tag.getAttributes();
        Iterator<String> it = hash.keySet().iterator();
     
        while(it.hasNext())  {
          TagLibTagAttr att=hash.get(it.next());
          if(!parent.containsAttribute(att.getName()) && att.hasDefaultValue())  {
             
            Attribute attr=new Attribute(tag.getAttributeType()==TagLibTag.ATTRIBUTE_TYPE_DYNAMIC,
                  att.getName(),
                  CastOther.toExpression(LitString.toExprString(Caster.toString(att.getDefaultValue(),null)),att.getType()),att.getType()
              );
              parent.addAttribute(attr);
          }
        }
      }
     
      boolean hasAttributeCollection=args.contains("attributecollection");
     
      // to less attributes
      if(!hasAttributeCollection && min>count)
        throw createTemplateException(data.cfml,"the tag "+tag.getFullName()+" must have at least "+min+" attributes",tag);
     
      // too much attributes
      if(!hasAttributeCollection && max>0 && max<count)
        throw createTemplateException(data.cfml,"the tag "+tag.getFullName()+" can have a maximum of "+max+" attributes",tag);
     
      // not defined attributes
      if(type==TagLibTag.ATTRIBUTE_TYPE_FIXED || type==TagLibTag.ATTRIBUTE_TYPE_MIXED)  {
        Map<String, TagLibTagAttr> hash = tag.getAttributes();
        Iterator<String> it = hash.keySet().iterator();
       
        while(it.hasNext())  {
          TagLibTagAttr att=hash.get(it.next());
          if(att.isRequired() && !args.contains(att.getName()) && att.getDefaultValue()==null)  {
            if(!hasAttributeCollection)throw createTemplateException(data.cfml,"attribute "+att.getName()+" is required for tag "+tag.getFullName(),tag);
            parent.addMissingAttribute(att.getName(),att.getType());
          }
        }
      }
      }
      catch(TemplateException te){
        data.cfml.setPos(start);
        // if the tag supports a non name attribute try this
        TagLibTagAttr sa = tag.getSingleAttr();
        if(sa!=null) attrNoName(parent,tag,data,sa);
        else throw te;
      }
    }
  // tag without attributes name
    else  {
      attrNoName(parent,tag,data,null);
    }
  }

    private static void attrNoName(Tag parent, TagLibTag tag, TagData data,TagLibTagAttr attr) throws TemplateException {
      if(attr==null)attr=tag.getFirstAttribute();
    String strName="noname";
    String strType="any";
    boolean pe=true;
    if(attr!=null) {
      strName=attr.getName();
      strType=attr.getType()
      pe=attr.getRtexpr();
    }
    //LitString.toExprString("",-1);
    Attribute att=new Attribute(false,strName,attributeValue(data,tag,strType,pe,true,NullExpression.NULL_EXPRESSION),strType);
    parent.addAttribute(att);
  }


  /**
     * Liest ein einzelnes Atribut eines tag ein (nicht NONAME).
     * <br />
     * EBNF:<br />
     * <code>attribute-name  spaces "=" spaces attribute-value;</code>
     * @param tag Definition des Tag das dieses Attribut enth¦lt.
     * @param args Container zum Speichern einzelner Attribute Namen zum nachtr¦glichen Prufen gegen die Tag-Lib.
     * @return Element Attribute Element.
     * @throws TemplateException
     */
    private static Attribute attribute(TagData data,TagLibTag tag, ArrayList<String> args,RefBoolean allowDefaultValue) throws TemplateException {
      Expression value=null;
     
      // Name
      StringBuffer sbType=new StringBuffer();
      RefBoolean dynamic=new RefBooleanImpl(false);
      boolean isDefaultValue=false;
      boolean[] parseExpression=new boolean[2];
      parseExpression[0]=true;
      parseExpression[1]=false;
      String name=attributeName(data.cfml,dynamic,args,tag,sbType,parseExpression,allowDefaultValue.toBooleanValue());
     
      // mixed in a noname attribute
      if(StringUtil.isEmpty(name)){
        allowDefaultValue.setValue(false);
        TagLibTagAttr attr = tag.getDefaultAttribute();
        if(attr==null)
          throw new TemplateException(data.cfml,"Invalid Identifier.");
        name=attr.getName();
        sbType.append(attr.getType());
        isDefaultValue=true;
      }
     
     
      comment(data.cfml,true);
     
      if(isDefaultValue || data.cfml.forwardIfCurrent('='))  {
        comment(data.cfml,true);
        // Value
        value=attributeValue(data,tag,sbType.toString(),parseExpression[0],false,LitString.toExprString(""))
      }
      // default value boolean true
      else {
        value=tag.getAttributeDefaultValue();
        if(sbType.toString().length()>0) {
          value=CastOther.toExpression(value, sbType.toString());
        }
      }   
      comment(data.cfml,true);
     
      return new Attribute(dynamic.toBooleanValue(),name,value,sbType.toString());
    }

  /**
   * Liest den Namen eines Attribut ein, je nach Attribut-Definition innerhalb der Tag-Lib,
   * wird der Name ber den identifier oder den Expression Transformer eingelesen.
   * <ul>
   * <li>FIX und DYNAMIC --> identifier </li>
   * <li>FULLDYNAMIC --> Expression Transformer </li>
   * </ul>
   * <br />
   * EBNF:<br />
   * <code>("expression"|'expression'|expression) | identifier;(* Ruft identifier oder den Expression Transformer auf je nach Attribute Definition in der Tag Lib. *)</code>
   * @param dynamic
   * @param args Container zum Speichern einzelner Attribute Namen zum nachtr¦glichen Prufen gegen die Tag-Lib.
   * @param tag Aktuelles tag aus der Tag-Lib
   * @param sbType Die Methode speichert innerhalb von sbType den Typ des Tags, zur Interpretation in der attribute Methode.
   * @param parseExpression Soll der Wert des Attributes geparst werden
   * @return Attribute Name
   * @throws TemplateException
   */
  private static String attributeName(CFMLString cfml,RefBoolean dynamic, ArrayList<String> args, TagLibTag tag,
      StringBuffer sbType, boolean[] parseExpression,boolean allowDefaultValue) throws TemplateException {
   
    String _id = identifier(cfml,!allowDefaultValue);
    if(StringUtil.isEmpty(_id)){
      return null;
    }
   
    int typeDef=tag.getAttributeType();
    String id=StringUtil.toLowerCase(_id);
        if(args.contains(id)) throw createTemplateException(cfml,"you can't use the same tag attribute ["+id+"] twice",tag);
    args.add(id);
   
    if("attributecollection".equals(id)){
      dynamic.setValue(tag.getAttribute(id)==null);
      sbType.append("struct");
      parseExpression[0]=true;
      parseExpression[1]=true;
    }
    else if(typeDef==TagLibTag.ATTRIBUTE_TYPE_FIXED || typeDef==TagLibTag.ATTRIBUTE_TYPE_MIXED) {
      TagLibTagAttr attr=tag.getAttribute(id);
      if(attr==null) {
        if(typeDef==TagLibTag.ATTRIBUTE_TYPE_FIXED) {
          String names=tag.getAttributeNames();
          if(StringUtil.isEmpty(names))
            throw createTemplateException(cfml,
                "Attribute "+id+" is not allowed for tag "+tag.getFullName(),tag);
         
          try{
            names=ListUtil.sort(names, "textnocase",null, null);
          }
          catch(Throwable t){}
          throw createTemplateException(cfml,
              "Attribute "+id+" is not allowed for tag "+tag.getFullName(),
              "valid attribute names are ["+names+"]",tag);
        }
        dynamic.setValue(true);
      }
      else {
        sbType.append(attr.getType());
        parseExpression[0]=attr.getRtexpr();
      }
    }
    else if(typeDef==TagLibTag.ATTRIBUTE_TYPE_DYNAMIC){
      dynamic.setValue(true);
    }
    return id;
  }
 
 
 
 
 


  /**
   * Liest den Wert eines Attribut, mithilfe des innerhalb der Tag-Lib definierten Expression Transformer, ein.
    * <br />
   * EBNF:<br />
   * <code>expression;</code>
   * @param tag
   * @param type
   * @param parseExpression
   * @param isNonName
   * @return Element Eingelesener bersetzer Wert des Attributes.
   * @throws TemplateException
   */
  public static Expression attributeValue(TagData data,TagLibTag tag, String type,boolean parseExpression,boolean isNonName, Expression noExpression) throws TemplateException {
    Expression expr;
    try {
      ExprTransformer transfomer=null;
      if(parseExpression){
          transfomer = tag.getTagLib().getExprTransfomer();
      }
      else  {
        if(data.getSimpleExprTransformer()==null) {
          data.setSimpleExprTransformer(new SimpleExprTransformer('#'));
          //set.setSpecialChar();
        }
        transfomer=data.getSimpleExprTransformer();
      }
      if(isNonName) {
          int pos=data.cfml.getPos();
          try {
          expr=transfomer.transform(data.page,data.ep,data.flibs,data.scriptTags,data.cfml,data.settings);
          }
          catch(TemplateException ete) {
             if(data.cfml.getPos()==pos)expr=noExpression;
             else throw ete;
          }
      }
      else expr=transfomer.transformAsString(data.page,data.ep,data.flibs,data.scriptTags,data.cfml,data.settings,true);
      if(type.length()>0) {
        expr=CastOther.toExpression(expr, type);
      }
    } catch (TagLibException e) {
      throw new TemplateException(data.cfml,e);
    }
    return expr;
  }
 
  /**
   * Liest einen Identifier ein und gibt diesen als String zurck.
    * <br />
   * EBNF:<br />
   * <code>(letter | "_") {letter | "_"|digit};</code>
   * @param throwError throw error or return null if name is invalid
   * @return Identifier String.
   * @throws TemplateException
   */
  public static String identifier(CFMLString cfml,boolean throwError) throws TemplateException  {
    int start = cfml.getPos();
    if(!cfml.isCurrentBetween('a','z') && !cfml.isCurrent('_')) {
      if(throwError)throw new TemplateException(cfml,"Invalid Identifier.");
      return null;
    }
    do {
      cfml.next();
      if(!(cfml.isCurrentBetween('a','z')
        || cfml.isCurrentBetween('0','9')
        || cfml.isCurrent('_')
        || cfml.isCurrent(':')
        || cfml.isCurrent('-'))) {
          break;
        }
    }
    while (cfml.isValidIndex());
    return cfml.substring(start,cfml.getPos()-start);
  }
 
 
  public static TemplateException createTemplateException(CFMLString cfml,String msg, String detail,TagLibTag tag) {
    TemplateException te = new TemplateException(cfml,msg,detail);
    setAddional(te,tag);
    return te;
  }
  public static TemplateException createTemplateException(CFMLString cfml,String msg, TagLibTag tag) {
    TemplateException te = new TemplateException(cfml,msg);
    setAddional(te,tag);
    return te;
  }
 
  public static TemplateException setAddional(TemplateException te, TagLibTag tlt) {
    setAddional((PageExceptionImpl)te, tlt);
    return te;
  }
 
  public static ApplicationException setAddional(ApplicationException ae, TagLibTag tlt) {
    setAddional((PageExceptionImpl)ae, tlt);
    return ae;
  }


  private static void setAddional(PageExceptionImpl pe, TagLibTag tlt) {
    Map<String, TagLibTagAttr> attrs = tlt.getAttributes();
    Iterator<Entry<String, TagLibTagAttr>> it = attrs.entrySet().iterator();
    Entry<String, TagLibTagAttr> entry;
    TagLibTagAttr attr;
   
    // Pattern
    StringBuilder pattern=new StringBuilder("<");
    pattern.append((tlt.getFullName()));
    StringBuilder req=new StringBuilder();
    StringBuilder opt=new StringBuilder();
    StringBuilder tmp;
   
   
    pattern.append(" ");
    int c=0;
    while(it.hasNext()){
      entry = it.next();
      attr=entry.getValue();
      tmp=attr.isRequired()?req:opt;
     
      tmp.append(" ");
      if(!attr.isRequired()) tmp.append("[");
      if(c++>0)pattern.append(" ");
      tmp.append(attr.getName());
      tmp.append("\"");
      tmp.append(attr.getType());
      tmp.append("\"");
      if(!attr.isRequired()) tmp.append("]");
    }

    if(req.length()>0)pattern.append(req);
    if(opt.length()>0)pattern.append(opt);
   
    if(tlt.getAttributeType()==TagLibTag.ATTRIBUTE_TYPE_MIXED || tlt.getAttributeType()==TagLibTag.ATTRIBUTE_TYPE_DYNAMIC)
      pattern.append(" ...");
    pattern.append(">");
    if(tlt.getHasBody()) {
      if(tlt.isBodyReq()){
        pattern.append("</");
        pattern.append(tlt.getFullName());
        pattern.append(">");
      }
      else if(tlt.isBodyFree()){
        pattern.append("[</");
        pattern.append(tlt.getFullName());
        pattern.append(">]");
      }
    }
   
    pe.setAdditional(KeyConstants._Pattern, pattern);
   
    // Documentation
    StringBuilder doc=new StringBuilder(tlt.getDescription());
    req=new StringBuilder();
    opt=new StringBuilder();
   
    doc.append("\n");
   
    it = attrs.entrySet().iterator();
    while(it.hasNext()){
      entry = it.next();
      attr=entry.getValue();
      tmp=attr.isRequired()?req:opt;
     
      tmp.append("* ");
      tmp.append(attr.getName());
      tmp.append(" (");
      tmp.append(attr.getType());
      tmp.append("): ");
      tmp.append(attr.getDescription());
      tmp.append("\n");
    }

    if(req.length()>0)doc.append("\nRequired:\n").append(req);
    if(opt.length()>0)doc.append("\nOptional:\n").append(opt);
   
    pe.setAdditional(KeyImpl.init("Documentation"), doc);
  }
 
}







TOP

Related Classes of railo.transformer.cfml.tag.CFMLTransformer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.