/*
* Copyright (C) 2000 - 2008 TagServlet Ltd
*
* This file is part of Open BlueDragon (OpenBD) CFML Server Engine.
*
* OpenBD is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Free Software Foundation,version 3.
*
* OpenBD is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenBD. If not, see http://www.gnu.org/licenses/
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this Program, or any covered work, by linking or combining
* it with any of the JARS listed in the README.txt (or a modified version of
* (that library), containing parts covered by the terms of that JAR, the
* licensors of this Program grant you additional permission to convey the
* resulting work.
* README.txt @ http://www.openbluedragon.org/license/README.txt
*
* http://www.openbluedragon.org/
*/
package com.naryx.tagfusion.cfm.tag;
import java.util.Stack;
import com.nary.util.FastMap;
import com.naryx.tagfusion.cfm.engine.cfEngine;
import com.naryx.tagfusion.cfm.file.cfmlFileCache;
/**
* This class is used mostly by the CUSTOM tags feature of tagServlet.
* Due to the fact that a TAG can have an optional end-tag, without
* any prior indication, we have to look through the rest of the file
* to see if we can see an end tag. If we find one, we pass that back
* to the core parsing engine. This class should only be used by the
* CFMODULE and all its subclasses.
*/
public class tagLocator extends cfTag {
private static final long serialVersionUID = 1L;
tagReader in;
String name, endname;
private static FastMap tagCache = new FastMap();
private Stack tagStack;
public tagLocator( String tagName, tagReader inFile ){
in = inFile;
name = tagName.toUpperCase();
endname = "/" + name;
in.mark();
tagStack = new Stack();
}
public String findEndMarker() {
String tagname = null;
int character;
int peekindx = 0;
while ((character = in.peekRead()) != -1) {
if (character == cfParseTag.TAG_START_MARKER) {
peekindx = in.getPeekIndx();
tagname = readTagName().toUpperCase();
boolean isEndTag = false;
if ( tagname.length() > 1 && tagname.charAt(0) == '/' ){
isEndTag = true;
tagname = tagname.substring(1);
}
//-- comment tag
if ( tagname.equals("!---") ) {
readCommentTag(in);
//-- cfml tag, custom tag or imported tag
}else if ( tagname.startsWith("CF") || cfmlFileCache.isCustomTag(tagname) || tagname.indexOf(':') != -1 ){
String restOfTag = readRestOfCFTag();
//-- self closing
if ( restOfTag.length() > 1 && restOfTag.charAt( restOfTag.length() - 2) == '/' ) {
in.setPeekIndx(peekindx);
//-- start tag
} else if ( !isEndTag ){
pushTagStack(tagname);
//-- end tag
}else{
if (endname.equalsIgnoreCase( "/" + tagname) && ( tagStack.size() == 0 || !tagStack.contains( tagname ) ) ){
// this has the same tag name and either the tagStack is empty
// or we aren't looking for another closing tag of the same name
return "<" + endname + ">";
// must have started the search in the body of another tag
} else if (tagStack.size() == 0) {
return null;
} else {
popTagStack(tagname);
}
}
}else{
in.setPeekIndx( peekindx );
}
}
}
return null;
}
private String readTagName(){
StringBuilder tagname = new StringBuilder();
int nextChar = in.peekRead();
while( nextChar != -1 && !cfParseTag.isTagNameTerminatingChar( nextChar ) && nextChar != cfParseTag.TAG_END_MARKER ){
tagname.append( (char) nextChar );
nextChar = in.peekRead();
}
in.setPeekIndx( in.getPeekIndx()-1 ); // leave it at the tagname terminating char
return tagname.toString();
}
private String readRestOfCFTag(){
StringBuilder restoftag = new StringBuilder();
int nextChar = in.peekRead();
StringBuilder blockChars = new StringBuilder();
while( nextChar != -1 ){
restoftag.append( (char) nextChar );
//--[ Check for the end marker if none has been specified
if ( nextChar == cfParseTag.CHAR_DOUBLEQUOTE || nextChar == cfParseTag.CHAR_SINGLEQUOTE || nextChar == cfParseTag.CHAR_HASH ){
if ( blockChars.length() > 0 ){
char lastChar = blockChars.charAt( blockChars.length()-1 );
if ( blockChars.length() > 0 && nextChar == lastChar ){
blockChars.deleteCharAt( blockChars.length()-1 );
}else if ( !( ( nextChar == cfParseTag.CHAR_DOUBLEQUOTE && lastChar == cfParseTag.CHAR_SINGLEQUOTE )
|| ( nextChar == cfParseTag.CHAR_SINGLEQUOTE && lastChar == cfParseTag.CHAR_DOUBLEQUOTE ) ) ){
blockChars.append( (char) nextChar );
}
}else{
blockChars.append( (char) nextChar );
}
}else if ( nextChar == cfParseTag.TAG_END_MARKER && blockChars.length() == 0 )
break;
nextChar = in.peekRead();
}
return restoftag.toString();
}
private void popTagStack( String _tagname ){
if ( tagStack.size() > 0 ){
// note that the end tag may not be at the top of the stack due to tags
// that have optional bodies. It's not til this point that we know there's
// no body
int popIndex = -1;
for ( int i = tagStack.size()-1; i >= 0 ; i-- ){
if ( ( (String) tagStack.elementAt(i) ).equals( _tagname ) ){
popIndex = i;
break;
}
}
if ( popIndex != -1 ){
for ( int i = tagStack.size()-1; i >= popIndex ; i-- ){
tagStack.pop();
}
}
}
}
private void pushTagStack( String _tagname ){
if ( cfmlFileCache.isCustomTag( _tagname ) || _tagname.indexOf(':') != -1 ){
tagStack.push( _tagname );
}else{
cfTag tagClass = (cfTag) tagCache.get( _tagname );
if ( tagClass == null ){
try{
tagClass = (cfTag) Class.forName( cfEngine.thisInstance.TagChecker.getClass( _tagname.toUpperCase() ) ).newInstance();
}catch(Exception ignored){} // let cfParseTag find this
}
// if we're expecting an end tag then push this on to the stack
if ( tagClass != null ){
// if optional end tag assume there is one
// note that cfPROCESSINGDIRECTIVE is included here as it's the only tag where the existance of
// an end tag is determined in defaultParameters() - a method that we can't invoke at this point
if ( tagClass instanceof cfOptionalBodyTag || tagClass.getEndMarker() != null || tagClass instanceof cfPROCESSINGDIRECTIVE ){
tagStack.push( _tagname );
}
}
}
}
private static void readCommentTag( tagReader inFile ){
StringBuilder tag = new StringBuilder(32);
int counter = 1;
int character;
while ( (character=inFile.peekRead()) != -1 ){
tag.append( (char)character );
if ( tag.toString().indexOf( "<!---" ) != -1 ){
counter++;
tag = new StringBuilder(32);
} else if ( tag.toString().indexOf( "--->" ) != -1 ){
--counter;
if ( counter == 0 )
break;
tag = new StringBuilder(32);
}
}
}
}