001/**************************************************************** 002 * Licensed to the Apache Software Foundation (ASF) under one * 003 * or more contributor license agreements. See the NOTICE file * 004 * distributed with this work for additional information * 005 * regarding copyright ownership. The ASF licenses this file * 006 * to you under the Apache License, Version 2.0 (the * 007 * "License"); you may not use this file except in compliance * 008 * with the License. You may obtain a copy of the License at * 009 * * 010 * http://www.apache.org/licenses/LICENSE-2.0 * 011 * * 012 * Unless required by applicable law or agreed to in writing, * 013 * software distributed under the License is distributed on an * 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 015 * KIND, either express or implied. See the License for the * 016 * specific language governing permissions and limitations * 017 * under the License. * 018 ****************************************************************/ 019 020package org.apache.james.mpt.protocol; 021 022import java.io.BufferedReader; 023import java.io.InputStream; 024import java.io.InputStreamReader; 025import java.io.Reader; 026import java.util.ArrayList; 027import java.util.List; 028import java.util.Properties; 029 030import org.apache.james.mpt.api.ProtocolInteractor; 031 032 033/** 034 * A builder which generates scripts from textual input. 035 * 036 * @author Darrell DeBoer <darrell@apache.org> 037 * 038 * @version $Revision$ 039 */ 040public class ProtocolSessionBuilder { 041 042 public static final String LOG = "LOG"; 043 044 public static final String WAIT = "WAIT"; 045 046 public static final String SERVER_CONTINUATION_TAG = "S: \\+"; 047 048 public static final String CLIENT_TAG = "C:"; 049 050 public static final String SERVER_TAG = "S:"; 051 052 public static final String OPEN_UNORDERED_BLOCK_TAG = "SUB {"; 053 054 public static final String CLOSE_UNORDERED_BLOCK_TAG = "}"; 055 056 public static final String COMMENT_TAG = "#"; 057 058 public static final String SESSION_TAG = "SESSION:"; 059 060 public static final String REINIT = "REINIT"; 061 062 public static final String TIMER = "TIMER"; 063 064 private final Properties variables; 065 066 public ProtocolSessionBuilder() { 067 variables = new Properties(); 068 } 069 070 /** 071 * Sets a substitution varaible. 072 * The value of a variable will be substituted whereever 073 * ${<code>NAME</code>} is found in the input 074 * where <code>NAME</code> is the name of the variable. 075 * @param name not null 076 * @param value not null 077 */ 078 public void setVariable(String name, String value) { 079 variables.put(name, value); 080 } 081 082 /** 083 * Builds a ProtocolSession by reading lines from the test file with the 084 * supplied name. 085 * 086 * @param fileName 087 * The name of the protocol session file. 088 * @return The ProtocolSession 089 */ 090 public ProtocolInteractor buildProtocolSession(String fileName) 091 throws Exception { 092 ProtocolInteractor session = new ProtocolSession(); 093 addTestFile(fileName, session); 094 return session; 095 } 096 097 /** 098 * Builds a ProtocolSession by reading lines from the reader. 099 * 100 * @param scriptName not null 101 * @param reader not null 102 * @return The ProtocolSession 103 */ 104 public ProtocolInteractor buildProtocolSession(String scriptName, Reader reader) 105 throws Exception { 106 ProtocolInteractor session = new ProtocolSession(); 107 addProtocolLines(scriptName, reader, session); 108 return session; 109 } 110 111 112 /** 113 * Adds all protocol elements from a test file to the ProtocolSession 114 * supplied. 115 * 116 * @param fileName 117 * The name of the protocol session file. 118 * @param session 119 * The ProtocolSession to add the elements to. 120 */ 121 public void addTestFile(String fileName, ProtocolInteractor session) 122 throws Exception { 123 // Need to find local resource. 124 InputStream is = this.getClass().getResourceAsStream(fileName); 125 if (is == null) { 126 throw new Exception("Test Resource '" + fileName + "' not found."); 127 } 128 129 addProtocolLines(fileName, is, session); 130 } 131 132 /** 133 * Reads ProtocolElements from the supplied InputStream and adds them to the 134 * ProtocolSession. 135 * @param scriptName 136 * The name of the source file, for error messages. 137 * @param is 138 * The input stream containing the protocol definition. 139 * @param session 140 * The ProtocolSession to add elements to. 141 */ 142 public void addProtocolLines(String scriptName, InputStream is, ProtocolInteractor session) throws Exception { 143 BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 144 145 doAddProtocolLines(session, scriptName, reader); 146 } 147 148 /** 149 * Reads ProtocolElements from the supplied Reader and adds them to the 150 * ProtocolSession. 151 * @param scriptName 152 * The name of the source file, for error messages. 153 * @param reader 154 * the reader containing the protocol definition. 155 * @param session 156 * The ProtocolSession to add elements to. 157 */ 158 public void addProtocolLines(String scriptName, Reader reader, ProtocolInteractor session) throws Exception { 159 final BufferedReader bufferedReader; 160 if (reader instanceof BufferedReader) { 161 bufferedReader = (BufferedReader) reader; 162 } else { 163 bufferedReader = new BufferedReader(reader); 164 } 165 doAddProtocolLines(session, scriptName, bufferedReader); 166 } 167 168 /** 169 * Reads ProtocolElements from the supplied Reader and adds them to the 170 * ProtocolSession. 171 * 172 * @param reader 173 * the reader containing the protocol definition. 174 * @param session 175 * The ProtocolSession to add elements to. 176 * @param scriptName 177 * The name of the source file, for error messages. 178 */ 179 private void doAddProtocolLines(ProtocolInteractor session, String scriptName, BufferedReader reader) throws Exception { 180 String line; 181 int sessionNumber = -1; 182 int lineNumber = -1; 183 String lastClientMsg = ""; 184 while ((line = reader.readLine()) != null) { 185 line = substituteVariables(line); 186 String location = scriptName + ":" + lineNumber; 187 if (SERVER_CONTINUATION_TAG.equals(line)) { 188 session.CONT(sessionNumber); 189 } else if (line.startsWith(CLIENT_TAG)) { 190 String clientMsg = ""; 191 if (line.length() > 3) { 192 clientMsg = line.substring(3); 193 } 194 session.CL(sessionNumber, clientMsg); 195 lastClientMsg = clientMsg; 196 } else if (line.startsWith(SERVER_TAG)) { 197 String serverMsg = ""; 198 if (line.length() > 3) { 199 serverMsg = line.substring(3); 200 } 201 session.SL(sessionNumber, serverMsg, location, lastClientMsg); 202 } else if (line.startsWith(OPEN_UNORDERED_BLOCK_TAG)) { 203 List<String> unorderedLines = new ArrayList<String>(5); 204 line = reader.readLine(); 205 206 while (!line.startsWith(CLOSE_UNORDERED_BLOCK_TAG)) { 207 if (!line.startsWith(SERVER_TAG)) { 208 throw new Exception( 209 "Only 'S: ' lines are permitted inside a 'SUB {' block."); 210 } 211 String serverMsg = line.substring(3); 212 unorderedLines.add(serverMsg); 213 line = reader.readLine(); 214 lineNumber++; 215 } 216 217 session.SUB(sessionNumber, unorderedLines, location, 218 lastClientMsg); 219 } else if (line.startsWith(COMMENT_TAG) 220 || line.trim().length() == 0) { 221 // ignore these lines. 222 } else if (line.startsWith(SESSION_TAG)) { 223 String number = line.substring(SESSION_TAG.length()).trim(); 224 if (number.length() == 0) { 225 throw new Exception("No session number specified"); 226 } 227 sessionNumber = Integer.parseInt(number); 228 } else { 229 String prefix = line; 230 if (line.length() > 3) { 231 prefix = line.substring(0, 3); 232 } 233 throw new Exception("Invalid line prefix: " + prefix); 234 } 235 lineNumber++; 236 } 237 } 238 239 /** 240 * Replaces ${<code>NAME</code>} with variable value. 241 * @param line not null 242 * @return not null 243 */ 244 private String substituteVariables(String line) { 245 if (variables.size() > 0) { 246 final StringBuffer buffer = new StringBuffer(line); 247 int start = 0; 248 int end = 0; 249 while (start >= 0 && end >= 0) { 250 start = buffer.indexOf("${", end); 251 if (start < 0) { 252 break; 253 } 254 end = buffer.indexOf("}", start); 255 if (end < 0) { 256 break; 257 } 258 final String name = buffer.substring(start+2, end); 259 final String value = variables.getProperty(name); 260 if (value != null) { 261 buffer.replace(start, end + 1, value); 262 final int variableLength = (end - start + 2); 263 end = end + (value.length() - variableLength); 264 } 265 } 266 line = buffer.toString(); 267 } 268 return line; 269 } 270 271}