001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2014 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 */ 022package org.granite.generator.gsp; 023 024import java.io.BufferedReader; 025import java.io.IOException; 026import java.io.Reader; 027import java.io.StringReader; 028import java.util.ArrayList; 029import java.util.List; 030 031import org.granite.generator.gsp.token.Comment; 032import org.granite.generator.gsp.token.Declaration; 033import org.granite.generator.gsp.token.Directive; 034import org.granite.generator.gsp.token.Expression; 035import org.granite.generator.gsp.token.Scriplet; 036import org.granite.generator.gsp.token.TemplateText; 037import org.granite.generator.gsp.token.Token; 038 039/** 040 * @author Franck WOLFF 041 */ 042public class Parser { 043 044 private final List<Token> elements = new ArrayList<Token>(); 045 046 public Parser() { 047 } 048 049 public List<Token> parse(Reader reader) throws IOException, ParseException { 050 if (reader == null) 051 throw new NullPointerException("reader cannot be null"); 052 if (!(reader instanceof StringReader || reader instanceof BufferedReader)) 053 reader = new BufferedReader(reader); 054 return parse(read(reader)); 055 } 056 057 public List<Token> getElements() { 058 return elements; 059 } 060 061 public void reset() { 062 elements.clear(); 063 } 064 065 private List<Token> parse(String template) throws ParseException { 066 if (template == null) 067 throw new NullPointerException("Argument template cannot be null"); 068 069 StringBuilder sb = new StringBuilder(64); 070 071 final int length = template.length(); 072 boolean inText = true; 073 for (int i = 0; i < length; i++) { 074 char first = inText ? '<' : '%'; 075 char last = inText ? '%' : '>'; 076 char c = template.charAt(i); 077 boolean appendC = true; 078 079 if (c == first) { // '<' (template text) or '%' (gsp tag content) 080 if (i+1 < length) { 081 c = template.charAt(i+1); 082 if (c == last) { // "<%" (template text) or "%>" (gsp tag content) 083 if (inText) 084 addTemplateText(i - sb.length(), sb.toString()); 085 else 086 addScriptingElement(i - sb.length() - 2, sb.toString()); 087 sb.setLength(0); 088 inText = !inText; 089 appendC = false; 090 i++; 091 } else if (c == '\\') { // "<\" (template text) or "%\" (gsp tag content) 092 sb.append(first); 093 for (i += 2; i < length && (c = template.charAt(i)) == '\\'; i++) 094 sb.append(c); 095 if (c != last) // add skiped first '/' 096 sb.append('\\'); 097 } else 098 c = first; 099 } 100 } 101 102 if (appendC) 103 sb.append(c); 104 } 105 106 if (!inText) 107 throw new ParseException("Missing script section end (\"%>\")"); 108 109 addTemplateText(length - sb.length(), sb.toString()); 110 111 return elements; 112 } 113 114 private String read(Reader reader) throws IOException { 115 StringBuilder sb = new StringBuilder(1024); 116 117 int pc = -1, c = -1; 118 while((c = reader.read()) != -1) { 119 switch (c) { 120 case '\r': // "\r[\n]" (Windows or Mac) 121 break; 122 case '\n': // "[\r]\n" (Windows or Unix) 123 sb.append('\n'); 124 break; 125 default: 126 if (pc == '\r') // "\r" (Mac) 127 sb.append('\n'); 128 sb.append((char)c); 129 break; 130 } 131 pc = c; 132 } 133 134 return sb.toString(); 135 } 136 137 private void addTemplateText(int index, String text) { 138 if (text.length() > 0) 139 elements.add(new TemplateText(index, text)); 140 } 141 142 private void addScriptingElement(int index, String text) throws ParseException { 143 if (text.length() == 0) 144 return; 145 char first = text.charAt(0); 146 switch (first) { 147 case '=': // expression 148 elements.add(new Expression(index, text.substring(1).trim())); 149 break; 150 case '!': // variable or method declaration 151 elements.add(new Declaration(index, text.substring(1).trim())); 152 break; 153 case '@': // directive 154 elements.add(new Directive(index, text.substring(1).trim())); 155 break; 156 case '-': // comment ? 157 if (text.startsWith("--") && text.endsWith("--")) { 158 elements.add(new Comment(index, text.substring(2, text.length() - 4))); 159 break; 160 } 161 // fall down (scriplet starting with '-')... 162 default: // scriplet 163 elements.add(new Scriplet(index, text)); 164 break; 165 } 166 } 167}