package railo.transformer.library.tag; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import org.objectweb.asm.Type; import railo.commons.lang.ClassException; import railo.commons.lang.ClassUtil; import railo.commons.lang.Md5; import railo.commons.lang.StringUtil; import railo.runtime.op.Caster; import railo.transformer.bytecode.Position; import railo.transformer.bytecode.cast.CastOther; import railo.transformer.bytecode.expression.Expression; import railo.transformer.bytecode.literal.LitBoolean; import railo.transformer.bytecode.literal.LitDouble; import railo.transformer.bytecode.literal.LitString; import railo.transformer.bytecode.statement.tag.Attribute; import railo.transformer.bytecode.statement.tag.Tag; import railo.transformer.bytecode.statement.tag.TagOther; import railo.transformer.cfml.attributes.AttributeEvaluator; import railo.transformer.cfml.attributes.AttributeEvaluatorException; import railo.transformer.cfml.evaluator.Evaluator; import railo.transformer.cfml.evaluator.EvaluatorException; import railo.transformer.cfml.tag.TagDependentBodyTransformer; /** * Die Klasse TagLibTag repaesentiert ein einzelne Tag Definition einer TagLib, * beschreibt also alle Informationen die man zum validieren eines Tags braucht. */ public final class TagLibTag { public final static int ATTRIBUTE_TYPE_FIXED=0; public final static int ATTRIBUTE_TYPE_DYNAMIC=1; public final static int ATTRIBUTE_TYPE_NONAME=3; public final static int ATTRIBUTE_TYPE_MIXED=4; /** * Definition des Attribut Type */ //public final static int ATTRIBUTE_TYPE_FULLDYNAMIC=2; deprecated /** * Definition des Attribut Type */ private final static Class[] CONSTRUCTOR_PARAMS=new Class[]{Position.class,Position.class}; private int attributeType; private String name; private boolean hasBody=true; private boolean isBodyReq=false; private boolean isTagDependent=false; private boolean bodyFree=true; private boolean parseBody; private boolean hasAppendix; private String description=""; private String tagClass; private String tteClass; private String tdbtClass; private int min; private int max; private TagLib tagLib; private Evaluator eval; private TagDependentBodyTransformer tdbt; private Map<String,TagLibTagAttr> attributes=new LinkedHashMap<String,TagLibTagAttr>(); private Map<String,String> setters=new HashMap<String,String>(); private TagLibTagAttr attrFirst; private TagLibTagAttr attrLast; private String strAttributeEvaluator; private AttributeEvaluator attributeEvaluator; private boolean handleException; private boolean hasDefaultValue=false; private Type tagType; private String tttClass; private Constructor tttConstructor; private boolean allowRemovingLiteral; private TagLibTagAttr defaultAttribute; private short status=TagLib.STATUS_IMPLEMENTED; private Class clazz; private TagLibTagScript script; private final static TagLibTagAttr UNDEFINED=new TagLibTagAttr(null); private TagLibTagAttr singleAttr=UNDEFINED; private Expression attributeDefaultValue; public TagLibTag duplicate(boolean cloneAttributes) { TagLibTag tlt = new TagLibTag(tagLib); tlt.attributeType=attributeType; tlt.name=name; tlt.hasBody=hasBody; tlt.isBodyReq=isBodyReq; tlt.isTagDependent=isTagDependent; tlt.bodyFree=bodyFree; tlt.parseBody=parseBody; tlt.hasAppendix=hasAppendix; tlt.description=description; tlt.tagClass=tagClass; tlt.tteClass=tteClass; tlt.tdbtClass=tdbtClass; tlt.min=min; tlt.max=max; tlt.strAttributeEvaluator=strAttributeEvaluator; tlt.handleException=handleException; tlt.hasDefaultValue=hasDefaultValue; tlt.tagType=tagType; tlt.tttClass=tttClass; tlt.tttConstructor=tttConstructor; tlt.allowRemovingLiteral=allowRemovingLiteral; tlt.status=status; tlt.eval=null; tlt.tdbt=null; tlt.attributeEvaluator=null; Iterator<Entry<String, TagLibTagAttr>> it = attributes.entrySet().iterator(); if(cloneAttributes) { while(it.hasNext()){ tlt.setAttribute(it.next().getValue().duplicate(tlt)); } if(defaultAttribute!=null)tlt.defaultAttribute=defaultAttribute.duplicate(tlt); } else { while(it.hasNext()){ tlt.setAttribute(it.next().getValue()); tlt.attrFirst=attrFirst; tlt.attrLast=attrLast; } tlt.defaultAttribute=defaultAttribute; } // setter Iterator<Entry<String, String>> sit = setters.entrySet().iterator(); Entry<String, String> se; while(sit.hasNext()){ se = sit.next(); tlt.setters.put(se.getKey(), se.getValue()); } /* private Map attributes=new HashMap(); private TagLibTagAttr attrFirst; private TagLibTagAttr attrLast; private Map setters=new HashMap(); private TagLibTagAttr defaultAttribute; */ return tlt; } /** * Geschuetzer Konstruktor ohne Argumente. * @param tagLib */ public TagLibTag(TagLib tagLib) { this.tagLib=tagLib; } /** * Gibt alle Attribute (TagLibTagAttr) eines Tag als HashMap zurueck. * @return HashMap Attribute als HashMap. */ public Map<String,TagLibTagAttr> getAttributes() { return attributes; } /** * Gibt ein bestimmtes Attribut anhand seines Namens zurueck, * falls dieses Attribut nicht existiert wird null zurueckgegeben. * @param name Name des Attribut das zurueckgegeben werden soll. * @return Attribute das angfragt wurde oder null. */ public TagLibTagAttr getAttribute(String name) { return attributes.get(name); } /** * Gibt das erste Attribut, welches innerhalb des Tag definiert wurde, zurueck. * @return Attribut das angfragt wurde oder null. */ public TagLibTagAttr getFirstAttribute() { return attrFirst; } /** * Gibt das letzte Attribut, welches innerhalb des Tag definiert wurde, zurueck. * @return Attribut das angfragt wurde oder null. */ public TagLibTagAttr getLastAttribute() { return attrLast; } /** * Gibt den Namen des Tag zurueck. * @return String Name des Tag. */ public String getName() { return name; } /** * Gibt den kompletten Namen des Tag zurueck, inkl. Name-Space und Trenner. * @return String Kompletter Name des Tag. */ public String getFullName() { String fullName; if(tagLib!=null) { fullName=tagLib.getNameSpaceAndSeparator()+name; } else { fullName=name; } return fullName; } /** * Gibt die Klassendefinition, welche diesen Tag implementiert, als Zeichenkette zurueck. * Achtung: Die implementierende Klasse ist keine Java Klasse. * @return String Zeichenkette der Klassendefinition. */ public String getTagClassName() { return tagClass; } public Class getClazz() throws ClassException { if(clazz==null) { clazz=ClassUtil.loadClass(tagClass); } return clazz; } public Type getTagType() throws ClassException { if(tagType==null) { tagType=Type.getType(getClazz()); } return tagType; } /** * @return the status (TagLib.,TagLib.STATUS_IMPLEMENTED,TagLib.STATUS_DEPRECATED,TagLib.STATUS_UNIMPLEMENTED) */ public short getStatus() { return status; } /** * @param status the status to set (TagLib.,TagLib.STATUS_IMPLEMENTED,TagLib.STATUS_DEPRECATED,TagLib.STATUS_UNIMPLEMENTED) */ public void setStatus(short status) { this.status = status; } /** * Gibt die Klassendefinition, der Klasse die den Evaluator (Translation Time Evaluator) implementiert, * als Zeichenkette zurueck. * Falls kein Evaluator definiert ist wird null zurueckgegeben. * @return String Zeichenkette der Klassendefinition. */ public String getTteClassName() { return tteClass; } public String getTttClassName() { return tttClass; } /** * Gibt den Evaluator (Translation Time Evaluator) dieser Klasse zurueck. * Falls kein Evaluator definiert ist, wird null zurueckgegeben. * @return Implementation des Evaluator zu dieser Klasse. * @throws EvaluatorException Falls die Evaluator-Klasse nicht geladen werden kann. */ public Evaluator getEvaluator() throws EvaluatorException { if(!hasTteClass()) return null; if(eval!=null) return eval; try { eval = (Evaluator) ClassUtil.loadInstance(tteClass); } catch (ClassException e) { throw new EvaluatorException(e.getMessage()); } return eval; } /** * Gibt den TagDependentBodyTransformer dieser Klasse zurueck. * Falls kein TagDependentBodyTransformer definiert ist, wird null zurueckgegeben. * @return Implementation des TagDependentBodyTransformer zu dieser Klasse. * @throws TagLibException Falls die TagDependentBodyTransformer-Klasse nicht geladen werden kann. */ public TagDependentBodyTransformer getBodyTransformer() throws TagLibException { if(!hasTdbtClass()) return null; if(tdbt!=null) return tdbt; try { tdbt = (TagDependentBodyTransformer) ClassUtil.loadInstance(tdbtClass); } catch (ClassException e) { throw new TagLibException(e); } return tdbt; } /** * Gibt zurueck ob Exception durch die implementierte Klasse abgehandelt werden oder nicht * @return Wird eine Exception abgehandelt? */ public boolean handleException() { return handleException; } /** * Gibt zurueck, ob eine Klassendefinition * der Klasse die den Evaluator (Translation Time Evaluator) implementiert existiert. * @return Ob eine Evaluator definiert ist. */ public boolean hasTteClass() { return tteClass !=null && tteClass.length()>0; } /** * Gibt zurueck, ob eine Klassendefinition * der Klasse die den TagDependentBodyTransformer implementiert existiert. * @return Ob eine Evaluator definiert ist. */ public boolean hasTdbtClass() { return tdbtClass !=null && tdbtClass.length()>0; } /** * Gibt den Attributetyp der Klasse zurueck. * ( ATTRIBUTE_TYPE_FIX, ATTRIBUTE_TYPE_DYNAMIC, ATTRIBUTE_TYPE_NONAME) * @return int */ public int getAttributeType() { return attributeType; } /** * Gibt zurueck, ob das Tag einen Body haben kann oder nicht. * @return Kann das Tag einen Body haben. */ public boolean getHasBody() { return hasBody; } /** * Gibt die maximale Anzahl Attribute zurueck, die das Tag haben kann. * @return Maximale moegliche Anzahl Attribute. */ public int getMax() { return max; } /** * Gibt die minimale Anzahl Attribute zurueck, die das Tag haben muss. * @return Minimal moegliche Anzahl Attribute. */ public int getMin() { return min; } /** * Gibt die TagLib zurueck zu der das Tag gehoert. * @return TagLib Zugehoerige TagLib. */ public TagLib getTagLib() { return tagLib; } /** * Gibt zurueck ob das Tag seinen Body parsen soll oder nicht. * @return Soll der Body geparst werden. */ public boolean getParseBody() { return parseBody; } /** * Gibt zurueck, ob das Tag einen Appendix besitzen kann oder nicht. * @return Kann das Tag einen Appendix besitzen. */ public boolean hasAppendix() { return hasAppendix; } /** * Fragt ab ob der Body eines Tag freiwillig ist oder nicht. * @return is required */ public boolean isBodyReq() { return isBodyReq; } /** * Fragt ab ob die verarbeitung des Inhaltes eines Tag mit einem eigenen Transformer * vorgenommen werden soll. * @return Fragt ab ob die verarbeitung des Inhaltes eines Tag mit einem eigenen Transformer * vorgenommen werden soll. */ public boolean isTagDependent() { return isTagDependent; } /** * Setzt die TagLib des Tag. * Diese Methode wird durch die Klasse TagLibFactory verwendet. * @param tagLib TagLib des Tag. */ protected void setTagLib(TagLib tagLib) { this.tagLib = tagLib; } /** * Setzt ein einzelnes Attribut (TagLibTagAttr) eines Tag. * Diese Methode wird durch die Klasse TagLibFactory verwendet. * @param attribute Attribute eines Tag. */ public void setAttribute(TagLibTagAttr attribute) { attributes.put(attribute.getName(),attribute); if(attrFirst==null)attrFirst=attribute; attrLast=attribute; } /** * Setzt den Attributtyp eines Tag. * ( ATTRIBUTE_TYPE_FIX, ATTRIBUTE_TYPE_DYNAMIC, ATTRIBUTE_TYPE_FULLDYNAMIC, ATTRIBUTE_TYPE_NONAME) * Diese Methode wird durch die Klasse TagLibFactory verwendet. * @param attributeType The attributeType to set */ public void setAttributeType(int attributeType) { this.attributeType = attributeType; } /** * Setzt die Information, was fuer ein BodyContent das Tag haben kann. * Diese Methode wird durch die Klasse TagLibFactory verwendet. * @param value BodyContent Information. */ public void setBodyContent(String value) { // empty, free, must, tagdependent value=value.toLowerCase().trim(); //if(value.equals("jsp")) value="free"; this.hasBody = !value.equals("empty"); this.isBodyReq = !value.equals("free"); this.isTagDependent = value.equals("tagdependent"); bodyFree=value.equals("free"); } /** * Setzt wieviele Attribute das Tag maximal haben darf. * Diese Methode wird durch die Klasse TagLibFactory verwendet. * @param max The max to set */ protected void setMax(int max) { this.max = max; } /** * Setzt wieviele Attribute das Tag minimal haben darf. * Diese Methode wird durch die Klasse TagLibFactory verwendet. * @param min The min to set */ protected void setMin(int min) { this.min = min; } /** * Setzt den Namen des Tag. * Diese Methode wird durch die Klasse TagLibFactory verwendet. * @param name Name des Tag. */ public void setName(String name) { this.name = name.toLowerCase(); } /** * Setzt die implementierende Klassendefinition des Tag. * Diese Methode wird durch die Klasse TagLibFactory verwendet. * @param tagClass Klassendefinition der Tag-Implementation. */ public void setTagClass(String tagClass) { this.tagClass = tagClass; } /** * Setzt die implementierende Klassendefinition des Evaluator. * Diese Methode wird durch die Klasse TagLibFactory verwendet. * @param tteClass Klassendefinition der Evaluator-Implementation. */ protected void setTteClass(String tteClass) { this.tteClass = tteClass; } /** * Setzt die implementierende Klassendefinition des Evaluator. * Diese Methode wird durch die Klasse TagLibFactory verwendet. * @param tteClass Klassendefinition der Evaluator-Implementation. */ public void setTttClass(String tttClass) { this.tttClass = tttClass; this.tttConstructor=null; } /** * Setzt die implementierende Klassendefinition des TagDependentBodyTransformer. * Diese Methode wird durch die Klasse TagLibFactory verwendet. * @param tdbtClass Klassendefinition der TagDependentBodyTransformer-Implementation. */ public void setTdbtClass(String tdbtClass) { this.tdbtClass = tdbtClass; this.tdbt = null; } /** * Setzt, ob der Body des Tag geparst werden soll oder nicht. * Diese Methode wird durch die Klasse TagLibFactory verwendet. * @param parseBody Soll der Body geparst werden. */ public void setParseBody(boolean parseBody) { this.parseBody = parseBody; } /** * Setzt ob das Tag einen Appendix besitzen kann oder nicht. * Diese Methode wird durch die Klasse TagLibFactory verwendet. * @param hasAppendix Kann das Tag einen Appendix besitzen. */ public void setAppendix(boolean hasAppendix) { this.hasAppendix = hasAppendix; } /** * @return Returns the description. */ public String getDescription() { return description; } /** * @param description The description to set. */ public void setDescription(String description) { this.description = description; } /** * @return Returns the bodyIsFree. */ public boolean isBodyFree() { return bodyFree; } public boolean hasBodyMethodExists() { Class clazz= ClassUtil.loadClass(getTagClassName(),(Class)null);//Class.orName(tag.getTagClassName()); if(clazz==null) return false; try { java.lang.reflect.Method method = clazz.getMethod("hasBody", new Class[]{boolean.class}); if(method==null)return false; return method.getReturnType()==void.class; } catch (Exception e) {} return false; } /** * @return Gibt zurueck ob ein Attribut Evaluator definiert ist oder nicht. */ public boolean hasAttributeEvaluator() { return strAttributeEvaluator!=null; } /** * @return Gibt den AttributeEvaluator zum Tag zurueck * @throws AttributeEvaluatorException */ public AttributeEvaluator getAttributeEvaluator() throws AttributeEvaluatorException { if(!hasAttributeEvaluator()) return null; if(attributeEvaluator!=null) return attributeEvaluator; try { return attributeEvaluator=(AttributeEvaluator) ClassUtil.loadInstance(strAttributeEvaluator); } catch (ClassException e) { throw new AttributeEvaluatorException(e.getMessage()); } } /** * Setzt den Namen der Klasse welche einen AttributeEvaluator implementiert. * @param value Name der AttributeEvaluator Klassse */ public void setAttributeEvaluatorClassName(String value) { strAttributeEvaluator=value; } /** * sets if tag handle exception inside his body or not * @param handleException handle it or not */ public void setHandleExceptions(boolean handleException) { this.handleException=handleException; } /** * @return */ public boolean hasDefaultValue() { return hasDefaultValue; } /** * @param hasDefaultValue The hasDefaultValue to set. */ public void setHasDefaultValue(boolean hasDefaultValue) { this.hasDefaultValue = hasDefaultValue; } /** * return ASM Tag for this tag * @param line * @return */ public Tag getTag(Position start,Position end) throws TagLibException { if(StringUtil.isEmpty(tttClass)) return new TagOther(start,end); try { return _getTag(start,end); } catch (ClassException e) { throw new TagLibException(e.getMessage()); } catch (NoSuchMethodException e) { throw new TagLibException(e.getMessage()); } catch (Throwable e) { throw new TagLibException(e); } } private Tag _getTag(Position start,Position end) throws ClassException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { if(tttConstructor==null) { Class clazz = ClassUtil.loadClass(tttClass); tttConstructor = clazz.getConstructor(CONSTRUCTOR_PARAMS); } return (Tag) tttConstructor.newInstance(new Object[]{start,end}); } public void setAllowRemovingLiteral(boolean allowRemovingLiteral) { this.allowRemovingLiteral=allowRemovingLiteral; } /** * @return the allowRemovingLiteral */ public boolean isAllowRemovingLiteral() { return allowRemovingLiteral; } public String getAttributeNames() { Iterator<String> it = attributes.keySet().iterator(); StringBuffer sb=new StringBuffer(); while(it.hasNext()) { if(sb.length()>0)sb.append(","); sb.append(it.next()); } return sb.toString(); } public String getSetter(Attribute attr, Type type) { if(tagLib.isCore()) return "set"+StringUtil.ucFirst(attr.getName()); String setter=setters.get(attr.getName()); if(setter!=null)return setter; setter = "set"+StringUtil.ucFirst(attr.getName()); Class clazz; try { if(type==null) type = CastOther.getType(attr.getType()); clazz=ClassUtil.loadClass(getTagClassName()); java.lang.reflect.Method m = ClassUtil.getMethodIgnoreCase(clazz,setter,new Class[]{ClassUtil.loadClass(type.getClassName())}); setter=m.getName(); } catch (Exception e) { //print.err(setter); e.printStackTrace(); } setters.put(attr.getName(), setter); return setter; } public String getHash() { StringBuffer sb=new StringBuffer(); sb.append(this.getTagClassName()); sb.append(this.getAttributeNames()); sb.append(this.getAttributeType()); sb.append(this.getMax()); sb.append(this.getMin()); sb.append(this.getName()); sb.append(this.getParseBody()); sb.append(this.getTteClassName()); sb.append(this.getTttClassName()); Iterator<Entry<String, TagLibTagAttr>> it = this.getAttributes().entrySet().iterator(); Entry<String, TagLibTagAttr> entry; while(it.hasNext()){ entry = it.next(); sb.append(entry.getKey()); sb.append(entry.getValue().getHash()); } try { return Md5.getDigestAsString(sb.toString()); } catch (IOException e) { return ""; } } public TagLibTagAttr getDefaultAttribute() { return defaultAttribute; } public void setDefaultAttribute(TagLibTagAttr defaultAttribute) { this.defaultAttribute=defaultAttribute; } public void setScript(TagLibTagScript script) { this.script=script; } /** * @return the script */ public TagLibTagScript getScript() { return script; } public TagLibTagAttr getSingleAttr() { if(singleAttr==UNDEFINED) { singleAttr=null; Iterator<TagLibTagAttr> it = getAttributes().values().iterator(); TagLibTagAttr attr; while(it.hasNext()){ attr=it.next(); if(attr.getNoname()){ singleAttr=attr; break; } } } return singleAttr; } /** * attribute value set, if the attribute has no value defined * @return */ public Expression getAttributeDefaultValue() { if(attributeDefaultValue==null) return LitBoolean.TRUE; return attributeDefaultValue; } public void setAttributeDefaultValue(String defaultValue) { defaultValue=defaultValue.trim(); // boolean if(StringUtil.startsWithIgnoreCase(defaultValue, "boolean:")) { String str=defaultValue.substring(8).trim(); Boolean b = Caster.toBoolean(str,null); if(b!=null){ this.attributeDefaultValue=LitBoolean.toExprBoolean(b.booleanValue()); return; } } // number else if(StringUtil.startsWithIgnoreCase(defaultValue, "number:")) { String str=defaultValue.substring(7).trim(); Double d = Caster.toDouble(str,null); if(d!=null){ this.attributeDefaultValue=LitDouble.toExprDouble(d.doubleValue()); return; } } else this.attributeDefaultValue=LitString.toExprString(defaultValue); } }