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