View Javadoc
1   package de.fhdw.wtf.parser;
2   
3   import java.util.Collection;
4   import java.util.List;
5   import java.util.Vector;
6   
7   import de.fhdw.wtf.common.ast.Attribute;
8   import de.fhdw.wtf.common.ast.Constructor;
9   import de.fhdw.wtf.common.ast.Name;
10  import de.fhdw.wtf.common.ast.Operation;
11  import de.fhdw.wtf.common.ast.type.ByNameState;
12  import de.fhdw.wtf.common.ast.type.ClassModifier;
13  import de.fhdw.wtf.common.ast.type.ClassType;
14  import de.fhdw.wtf.common.ast.type.ExceptionClassType;
15  import de.fhdw.wtf.common.ast.type.Type;
16  import de.fhdw.wtf.common.ast.type.TypeProxy;
17  import de.fhdw.wtf.common.exception.parser.AbstractParserException;
18  import de.fhdw.wtf.common.exception.parser.NoCurlyBracketOpenException;
19  import de.fhdw.wtf.common.exception.parser.NoEqualException;
20  import de.fhdw.wtf.common.exception.parser.NoPlusSymbolException;
21  import de.fhdw.wtf.common.exception.parser.NoSemicolonException;
22  import de.fhdw.wtf.common.stream.TokenStream;
23  import de.fhdw.wtf.common.token.IdentifierToken;
24  import de.fhdw.wtf.common.token.Token;
25  
26  /**
27   * Parser to parse a given {@link TokenStream}.
28   * 
29   * * TODO -> Duplicate code. See RegularClassParser. As a first try just copied ClassParser to ExceptionClassParser and
30   * RegularClassParser. Think about how to split them.
31   */
32  public final class ExceptionClassParser {
33  	
34  	/**
35  	 * Tokenstream.
36  	 */
37  	private final TokenStream stream;
38  	
39  	/**
40  	 * Collection for exceptions.
41  	 */
42  	private final Collection<AbstractParserException> exceptions;
43  	
44  	/**
45  	 * Constructor of {@link ExceptionClassParser}.
46  	 * 
47  	 * @param stream
48  	 *            tokenstream
49  	 * @param exceptions
50  	 *            collection for exceptions
51  	 */
52  	private ExceptionClassParser(final TokenStream stream, final Collection<AbstractParserException> exceptions) {
53  		this.stream = stream;
54  		this.exceptions = exceptions;
55  	}
56  	
57  	/**
58  	 * Creates a {@link ExceptionClassParser}-Object.
59  	 * 
60  	 * @param stream
61  	 *            tokenstream
62  	 * @param exceptions
63  	 *            collection for exceptions
64  	 * @return the {@link ExceptionClassParser}-Object.
65  	 */
66  	protected static ExceptionClassParser create(final TokenStream stream,
67  			final Collection<AbstractParserException> exceptions) {
68  		return new ExceptionClassParser(stream, exceptions);
69  	}
70  	
71  	/**
72  	 * Parses a Class with <code>name</code> as the class name from this.stream. This method expects the first Token in
73  	 * the stream to be an EqualToken. This method calls an operation to parse the Attributes and ClassModifiers. If one
74  	 * of the Tokens in the stream is unexpected, an AbstractParserException will be thrown.
75  	 * 
76  	 * @param name
77  	 *            : Name of Class.
78  	 * @param firstToken
79  	 *            : First token of class.
80  	 * @throws AbstractParserException
81  	 *             AbstractParserException
82  	 * @return ClassType
83  	 */
84  	protected ClassType parse(final Name name, final Token firstToken) throws AbstractParserException {
85  		try {
86  			ParserUtils.requireAndRemoveEqualToken(this.stream);
87  		} catch (final NoEqualException e) {
88  			throw e;
89  		}
90  		
91  		final Collection<Attribute> attributes = new Vector<>();
92  		final Collection<ClassModifier> modifiers = new Vector<>();
93  		final Collection<ClassType> subTypes = new Vector<>();
94  		final Collection<Operation> operations = new Vector<>();
95  		final List<Type> superTypes = new Vector<>();
96  		final Collection<Constructor> constructors = new Vector<>();
97  		final ClassType classResult =
98  				ExceptionClassType.create(
99  						name,
100 						modifiers,
101 						attributes,
102 						superTypes,
103 						operations,
104 						constructors,
105 						firstToken,
106 						subTypes);
107 		try {
108 			classResult.getSuperTypes().addAll(this.parseSuperTypes());
109 			ParserUtils.requireAndRemoveCurlyBracketOpenToken(this.stream);
110 		} catch (final NoCurlyBracketOpenException e) {
111 			this.exceptions.add(e);
112 			// Don't skip to next semicolon because you may find it behind the class so that
113 			// you produce more exceptions as necessary.
114 		}
115 		while (this.stream.hasNext() && !this.checkClassEnd() && !(this.stream.peek().isEndToken())) {
116 			final Token currentToken = this.stream.peek();
117 			if (currentToken.isIdentifierToken()) {
118 				try {
119 					final IdentifierToken token = ParserUtils.requireAndRemoveIdentifier(this.stream);
120 					this.parseClassElement(
121 							classResult,
122 							constructors,
123 							attributes,
124 							operations,
125 							token.stringRepresentation(),
126 							classResult,
127 							token);
128 				} catch (final NoSemicolonException e) {
129 					throw e;
130 				} catch (final AbstractParserException e) {
131 					this.exceptions.add(e);
132 					ParserUtils.skipToSemicolonToken(this.stream);
133 					continue;
134 				}
135 			} else if (currentToken.isBracketOpenToken()) {
136 				this.parseConstructor(currentToken, constructors, classResult);
137 			} else {
138 				ParserUtils.requireAndRemoveCurlyBracketCloseToken(this.stream);
139 			}
140 		}
141 		modifiers.addAll(ClassModifierParser.create(this.stream, this.exceptions).parse(classResult));
142 		classResult.setLastToken(this.stream.peek());
143 		return classResult;
144 	}
145 	
146 	/**
147 	 * Parses a constructor.
148 	 * 
149 	 * @param firstToken
150 	 *            firstToken.
151 	 * @param constructors
152 	 *            the list of constructors for classResult.
153 	 * @param owner
154 	 *            the owner class of the constructor.
155 	 * @throws AbstractParserException
156 	 *             if the syntax is not correct.
157 	 */
158 	private void parseConstructor(final Token firstToken,
159 			final Collection<Constructor> constructors,
160 			final ClassType owner) throws AbstractParserException {
161 		constructors.add(ConstructorParser.create(this.stream, this.exceptions).parse(firstToken, owner));
162 	}
163 	
164 	/**
165 	 * Parses a series of tokens and returns the a list of {@link TypeProxy} . If the series of tokens does not match
166 	 * the grammar an Exception will be thrown.
167 	 * 
168 	 * @return super types
169 	 * @throws AbstractParserException
170 	 *             {@link NoPlusSymbolException}: if there are some {@link IdentifierToken}s or an
171 	 *             {@link IdentifierToken} followed by an {@link de.fhdw.wtf.common.token.symbols.CurlyBracketOpenToken}
172 	 *             with no symbol between, than there is a PlusSymbol missing. {@link NoCurlyBracketOpenException}: If
173 	 *             it ist not a {@link NoPlusSymbolException} it must be a {@link NoCurlyBracketOpenException}.
174 	 * 
175 	 */
176 	private Collection<TypeProxy> parseSuperTypes() throws AbstractParserException {
177 		final Collection<TypeProxy> superTypes = new Vector<>();
178 		
179 		tryPossibleExceptions(this.stream.copy());
180 		
181 		while (this.stream.hasNext() && this.stream.peek().isIdentifierToken()) {
182 			final NameParser nameParser = NameParser.createNameParser(this.stream);
183 			final Name currentSuperTypeName = nameParser.parse();
184 			
185 			ParserUtils.requireAndRemovePlusSymbol(this.stream);
186 			final ByNameState byName = ByNameState.create(currentSuperTypeName);
187 			final TypeProxy typeProxy = TypeProxy.create(currentSuperTypeName.getFirstToken(), byName);
188 			typeProxy.setLastToken(currentSuperTypeName.getLastToken());
189 			superTypes.add(typeProxy);
190 		}
191 		return superTypes;
192 	}
193 	
194 	/**
195 	 * Trys if there are any excpetions with missing + or {. In the case of missing { it is necessary to start the
196 	 * parsing again from the first identifier. For example: Class:class=attribute:String;};
197 	 * 
198 	 * @param copy
199 	 *            - a copy of the origin tokenStream
200 	 * @throws AbstractParserException
201 	 *             {@link NoPlusSymbolException} and {@link NoCurlyBracketOpenException}.
202 	 */
203 	private static void tryPossibleExceptions(final TokenStream copy) throws AbstractParserException {
204 		final Token firstPosition = copy.peek();
205 		
206 		try {
207 			while (copy.hasNext() && copy.peek().isIdentifierToken()) {
208 				final NameParser nameParser = NameParser.createNameParser(copy);
209 				nameParser.parse();
210 				ParserUtils.requireAndRemovePlusSymbol(copy);
211 			}
212 		} catch (final NoPlusSymbolException e) {
213 			if (copy.hasNext() && (copy.peek().isIdentifierToken() || copy.peek().isCurlyBracketOpenToken())) {
214 				throw e;
215 			} else {
216 				throw NoCurlyBracketOpenException.create(firstPosition);
217 			}
218 		}
219 	}
220 	
221 	/**
222 	 * Parses a series of Tokens and creates attribute or operation. The name it is <code>name</code>. The result will
223 	 * be add to <code>attributes</code> or <code>operations</code>. The first {@link Token} is <code>firstToken</code>.
224 	 * be <code>attributeName</code>.
225 	 * 
226 	 * @param classResult
227 	 *            the class that is being parsed at the moment.
228 	 * 
229 	 * @param attributes
230 	 *            attributes
231 	 * @param operations
232 	 *            operations
233 	 * @param constructors
234 	 *            constructors
235 	 * @param name
236 	 *            name of attribute or operation
237 	 * @param firstToken
238 	 *            firstToken
239 	 * @throws AbstractParserException
240 	 *             AbstractParserException
241 	 */
242 	private void parseClassElement(final ClassType classResult,
243 			final Collection<Constructor> constructors,
244 			final Collection<Attribute> attributes,
245 			final Collection<Operation> operations,
246 			final String name,
247 			final ClassType containingType,
248 			final IdentifierToken firstToken) throws AbstractParserException {
249 		ParserUtils.requireAndRemoveColonToken(this.stream);
250 		final Token nextToken = this.stream.peek();
251 		if (nextToken.isDoubleSquareBracketOpenToken()) {
252 			this.stream.removeFirst();
253 			operations
254 					.add(OperationParser.create(this.stream, this.exceptions).parse(name, containingType, firstToken));
255 		} else {
256 			attributes.add(AttributeParser.create(this.stream, this.exceptions).parse(name, firstToken));
257 		}
258 		
259 	}
260 	
261 	/**
262 	 * Returns true if the next Token of this.stream is a CurlyBracketCloseToken, otherwise false.
263 	 * 
264 	 * @return Boolean
265 	 */
266 	private boolean checkClassEnd() {
267 		if (!(this.stream.peek().isCurlyBracketCloseToken())) {
268 			return false;
269 		}
270 		this.stream.removeFirst();
271 		return true;
272 	}
273 }