001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2007-2010 ADEQUATE SYSTEMS SARL
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
021 package org.granite.generator.gsp;
022
023 import java.io.BufferedReader;
024 import java.io.IOException;
025 import java.io.Reader;
026 import java.io.StringReader;
027 import java.util.ArrayList;
028 import java.util.List;
029
030 import org.granite.generator.gsp.token.Comment;
031 import org.granite.generator.gsp.token.Declaration;
032 import org.granite.generator.gsp.token.Directive;
033 import org.granite.generator.gsp.token.Expression;
034 import org.granite.generator.gsp.token.Scriplet;
035 import org.granite.generator.gsp.token.TemplateText;
036 import org.granite.generator.gsp.token.Token;
037
038 /**
039 * @author Franck WOLFF
040 */
041 public 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 }