001/* 002 GRANITE DATA SERVICES 003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S. 004 005 This file is part of Granite Data Services. 006 007 Granite Data Services is free software; you can redistribute it and/or modify 008 it under the terms of the GNU Library General Public License as published by 009 the Free Software Foundation; either version 2 of the License, or (at your 010 option) any later version. 011 012 Granite Data Services is distributed in the hope that it will be useful, but 013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License 015 for more details. 016 017 You should have received a copy of the GNU Library General Public License 018 along with this library; if not, see <http://www.gnu.org/licenses/>. 019*/ 020 021package org.granite.generator.gsp; 022 023import java.io.BufferedReader; 024import java.io.IOException; 025import java.io.Reader; 026import java.io.StringReader; 027import java.util.ArrayList; 028import java.util.List; 029 030import org.granite.generator.gsp.token.Comment; 031import org.granite.generator.gsp.token.Declaration; 032import org.granite.generator.gsp.token.Directive; 033import org.granite.generator.gsp.token.Expression; 034import org.granite.generator.gsp.token.Scriplet; 035import org.granite.generator.gsp.token.TemplateText; 036import org.granite.generator.gsp.token.Token; 037 038/** 039 * @author Franck WOLFF 040 */ 041public class Parser { 042 043 private final List<Token> elements = new ArrayList<Token>(); 044 045 public Parser() { 046 } 047 048 public List<Token> parse(Reader reader) throws IOException, ParseException { 049 if (reader == null) 050 throw new NullPointerException("reader cannot be null"); 051 if (!(reader instanceof StringReader || reader instanceof BufferedReader)) 052 reader = new BufferedReader(reader); 053 return parse(read(reader)); 054 } 055 056 public List<Token> getElements() { 057 return elements; 058 } 059 060 public void reset() { 061 elements.clear(); 062 } 063 064 private List<Token> parse(String template) throws ParseException { 065 if (template == null) 066 throw new NullPointerException("Argument template cannot be null"); 067 068 StringBuilder sb = new StringBuilder(64); 069 070 final int length = template.length(); 071 boolean inText = true; 072 for (int i = 0; i < length; i++) { 073 char first = inText ? '<' : '%'; 074 char last = inText ? '%' : '>'; 075 char c = template.charAt(i); 076 boolean appendC = true; 077 078 if (c == first) { // '<' (template text) or '%' (gsp tag content) 079 if (i+1 < length) { 080 c = template.charAt(i+1); 081 if (c == last) { // "<%" (template text) or "%>" (gsp tag content) 082 if (inText) 083 addTemplateText(i - sb.length(), sb.toString()); 084 else 085 addScriptingElement(i - sb.length() - 2, sb.toString()); 086 sb.setLength(0); 087 inText = !inText; 088 appendC = false; 089 i++; 090 } else if (c == '\\') { // "<\" (template text) or "%\" (gsp tag content) 091 sb.append(first); 092 for (i += 2; i < length && (c = template.charAt(i)) == '\\'; i++) 093 sb.append(c); 094 if (c != last) // add skiped first '/' 095 sb.append('\\'); 096 } else 097 c = first; 098 } 099 } 100 101 if (appendC) 102 sb.append(c); 103 } 104 105 if (!inText) 106 throw new ParseException("Missing script section end (\"%>\")"); 107 108 addTemplateText(length - sb.length(), sb.toString()); 109 110 return elements; 111 } 112 113 private String read(Reader reader) throws IOException { 114 StringBuilder sb = new StringBuilder(1024); 115 116 int pc = -1, c = -1; 117 while((c = reader.read()) != -1) { 118 switch (c) { 119 case '\r': // "\r[\n]" (Windows or Mac) 120 break; 121 case '\n': // "[\r]\n" (Windows or Unix) 122 sb.append('\n'); 123 break; 124 default: 125 if (pc == '\r') // "\r" (Mac) 126 sb.append('\n'); 127 sb.append((char)c); 128 break; 129 } 130 pc = c; 131 } 132 133 return sb.toString(); 134 } 135 136 private void addTemplateText(int index, String text) { 137 if (text.length() > 0) 138 elements.add(new TemplateText(index, text)); 139 } 140 141 private void addScriptingElement(int index, String text) throws ParseException { 142 if (text.length() == 0) 143 return; 144 char first = text.charAt(0); 145 switch (first) { 146 case '=': // expression 147 elements.add(new Expression(index, text.substring(1).trim())); 148 break; 149 case '!': // variable or method declaration 150 elements.add(new Declaration(index, text.substring(1).trim())); 151 break; 152 case '@': // directive 153 elements.add(new Directive(index, text.substring(1).trim())); 154 break; 155 case '-': // comment ? 156 if (text.startsWith("--") && text.endsWith("--")) { 157 elements.add(new Comment(index, text.substring(2, text.length() - 4))); 158 break; 159 } 160 // fall down (scriplet starting with '-')... 161 default: // scriplet 162 elements.add(new Scriplet(index, text)); 163 break; 164 } 165 } 166}