/**
* Reads a input stream, and writes the data with replaced templates on a output stream
*/
private final static byte[] writeTemplate(final InputStream in, final OutputStream out, final Map<String, String> pattern, final byte[] dflt, final byte[] prefix) throws IOException {
final PushbackInputStream pis = new PushbackInputStream(in, 100);
final ByteArrayOutputStream keyStream = new ByteArrayOutputStream(512);
byte[] key;
byte[] multi_key;
byte[] replacement;
int bb;
final ByteBuffer structure = new ByteBuffer();
while (transferUntil(pis, out, hashChar)) {
bb = pis.read();
keyStream.reset();
// #{
if ((bb & 0xFF) == lcbr) { //multi
if (transferUntil(pis, keyStream, mClose)) { //close tag
//multi_key = "_" + keyStream.toString(); //for _Key
bb = pis.read();
if ((bb & 0xFF) != 10){ //kill newline
pis.unread(bb);
}
multi_key = keyStream.toByteArray(); //IMPORTANT: no prefix here
keyStream.reset(); //reset stream
//this needs multi_key without prefix
if (transferUntil(pis, keyStream, appendBytes(mOpen, slashChar, multi_key, mClose))){
bb = pis.read();
if((bb & 0xFF) != 10){ //kill newline
pis.unread(bb);
}
final byte[] text=keyStream.toByteArray(); //text between #{key}# an #{/key}#
int num=0;
final String patternKey = getPatternKey(prefix, multi_key);
if(pattern.containsKey(patternKey) && pattern.get(patternKey) != null){
try{
num=Integer.parseInt(pattern.get(patternKey)); // Key contains the iteration number as string
}catch(final NumberFormatException e){
num=0;
}
}
structure.append('<')
.append(multi_key)
.append(multi_num)
.append(ASCII.getBytes(Integer.toString(num)))
.append(close_quotetagn);
for(int i=0;i < num;i++) {
final PushbackInputStream pis2 = new PushbackInputStream(new ByteArrayInputStream(text));
//System.out.println("recursing with text(prefix="+ multi_key + "_" + i + "_" +"):"); //DEBUG
//System.out.println(text);
structure.append(writeTemplate(pis2, out, pattern, dflt, newPrefix(prefix,multi_key,i)));
}//for
structure.append(open_endtag).append(multi_key).append(close_tagn);
} else {//transferUntil
Log.logSevere("TEMPLATE", "No Close Key found for #{"+UTF8.String(multi_key)+"}#"); //prefix here?
}
}
// #(
} else if ((bb & 0xFF) == lrbr) { //alternative
int others=0;
final ByteBuffer text= new ByteBuffer();
transferUntil(pis, keyStream, aClose);
key = keyStream.toByteArray(); //Caution: Key does not contain prefix
keyStream.reset(); //clear
boolean byName=false;
int whichPattern=0;
byte[] patternName = new byte[0];
final String patternKey = getPatternKey(prefix, key);
if(pattern.containsKey(patternKey) && pattern.get(patternKey) != null){
final String patternId=pattern.get(patternKey);
try{
whichPattern=Integer.parseInt(patternId); //index
}catch(final NumberFormatException e){
whichPattern=0;
byName=true;
patternName = UTF8.getBytes(patternId);
}
}
int currentPattern=0;
boolean found=false;
keyStream.reset(); //reset stream
PushbackInputStream pis2;
if (byName) {
//TODO: better Error Handling
transferUntil(pis, keyStream, appendBytes(PP, patternName, null, null));
if(pis.available()==0){
Log.logSevere("TEMPLATE", "No such Template: %%" + UTF8.String(patternName));
return structure.getBytes();
}
keyStream.reset();
transferUntil(pis, keyStream, dpdpa);
pis2 = new PushbackInputStream(new ByteArrayInputStream(keyStream.toByteArray()));
structure.append(writeTemplate(pis2, out, pattern, dflt, newPrefix(prefix,key)));
transferUntil(pis, keyStream, appendBytes(hash_brackopen_slash, key, brackclose_hash, null));
if(pis.available()==0){
Log.logSevere("TEMPLATE", "No Close Key found for #("+UTF8.String(key)+")# (by Name)");
}
} else {
while(!found){
bb=pis.read(); // performance problem? trace always points to this line
if ((bb & 0xFF) == hashChar){
bb=pis.read();
if ((bb & 0xFF) == lrbr){
transferUntil(pis, keyStream, aClose);
//reached the end. output last string.
if (java.util.Arrays.equals(keyStream.toByteArray(),appendBytes(slashChar, key, null,null))) {
pis2 = new PushbackInputStream(new ByteArrayInputStream(text.getBytes()));
//this maybe the wrong, but its the last
structure.append('<').append(key).append(alternative_which).append(ASCII.getBytes(Integer.toString(whichPattern))).append(ASCII.getBytes("\" found=\"0\">\n"));
structure.append(writeTemplate(pis2, out, pattern, dflt, newPrefix(prefix,key)));
structure.append(open_endtag).append(key).append(close_tagn);
found=true;
}else if(others >0 && keyStream.toString().startsWith("/")){ //close nested
others--;
text.append(aOpen).append(keyStream.toByteArray()).append(brackclose_hash);
} else { //nested
others++;
text.append(aOpen).append(keyStream.toByteArray()).append(brackclose_hash);
}
keyStream.reset(); //reset stream
continue;
} //is not #(
pis.unread(bb);//is processed in next loop
bb = (hashChar);//will be added to text this loop
//text += "#";
}else if ((bb & 0xFF) == ':' && others==0){//ignore :: in nested Expressions
bb=pis.read();
if ((bb & 0xFF) == ':'){
if(currentPattern == whichPattern){ //found the pattern
pis2 = new PushbackInputStream(new ByteArrayInputStream(text.getBytes()));
structure.append('<').append(key).append(alternative_which).append(ASCII.getBytes(Integer.toString(whichPattern))).append(ASCII.getBytes("\" found=\"0\">\n"));
structure.append(writeTemplate(pis2, out, pattern, dflt, newPrefix(prefix,key)));
structure.append(open_endtag).append(key).append(close_tagn);
transferUntil(pis, keyStream, appendBytes(hash_brackopen_slash, key, brackclose_hash,null));//to #(/key)#.
found=true;
}
currentPattern++;
text.clear();
continue;
}
text.append(':');
}
if(!found){
text.append((byte)bb);/*
if(pis.available()==0){
serverLog.logSevere("TEMPLATE", "No Close Key found for #("+UTF8.String(key)+")# (by Index)");
found=true;
}*/
}
}//while
}//if(byName) (else branch)
// #[
} else if ((bb & 0xFF) == lbr) { //normal
if (transferUntil(pis, keyStream, pClose)) {
// pattern detected, write replacement
key = keyStream.toByteArray();
final String patternKey = getPatternKey(prefix, key);
replacement = replacePattern(patternKey, pattern, dflt); //replace
structure.append('<').append(key)
.append(ASCII.getBytes(" type=\"normal\">\n"));
structure.append(replacement);
structure.append(ASCII.getBytes("</")).append(key)
.append(close_tagn);
FileUtils.copy(replacement, out);
} else {
// inconsistency, simply finalize this
FileUtils.copy(pis, out);
return structure.getBytes();
}
// #%
} else if ((bb & 0xFF) == pcChar) { //include
final ByteBuffer include = new ByteBuffer();
keyStream.reset(); //reset stream
if(transferUntil(pis, keyStream, iClose)){
byte[] filename = keyStream.toByteArray();
//if(filename.startsWith( Character.toString((char)lbr) ) && filename.endsWith( Character.toString((char)rbr) )){ //simple pattern for filename
if((filename[0] == lbr) && (filename[filename.length-1] == rbr)){ //simple pattern for filename
final byte[] newFilename = new byte[filename.length-2];
System.arraycopy(filename, 1, newFilename, 0, newFilename.length);
final String patternkey = getPatternKey(prefix, newFilename);
filename= replacePattern(patternkey, pattern, dflt);
}
if (filename.length > 0 && !java.util.Arrays.equals(filename, dflt)) {
BufferedReader br = null;
try{
//br = new BufferedReader(new InputStreamReader(new FileInputStream( filename ))); //Simple Include
br = new BufferedReader( new InputStreamReader(new FileInputStream( HTTPDFileHandler.getLocalizedFile(UTF8.String(filename))),"UTF-8") ); //YaCy (with Locales)
//Read the Include
String line = "";
while ((line = br.readLine()) != null) {
include.append(UTF8.getBytes(line)).append(ASCII.getBytes(de.anomic.server.serverCore.CRLF_STRING));
}
} catch (final IOException e) {
//file not found?
Log.logSevere("FILEHANDLER","Include Error with file " + UTF8.String(filename) + ": " + e.getMessage());
} finally {
if (br != null) try { br.close(); br=null; } catch (final Exception e) {}
}
final PushbackInputStream pis2 = new PushbackInputStream(new ByteArrayInputStream(include.getBytes()));
structure.append(ASCII.getBytes("<fileinclude file=\"")).append(filename).append(close_tagn);
structure.append(writeTemplate(pis2, out, pattern, dflt, prefix));
structure.append(ASCII.getBytes("</fileinclude>\n"));
}
}