View Javadoc
1   package de.fhdw.wtf.parser;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.Iterator;
6   
7   import de.fhdw.wtf.common.ast.type.ClassModifier;
8   import de.fhdw.wtf.common.ast.type.ClassModifierAbstract;
9   import de.fhdw.wtf.common.ast.type.ClassModifierService;
10  import de.fhdw.wtf.common.ast.type.ClassModifierTransient;
11  import de.fhdw.wtf.common.ast.type.ClassModifierVisitable;
12  import de.fhdw.wtf.common.ast.type.ClassType;
13  import de.fhdw.wtf.common.ast.visitor.ClassModifierVisitor;
14  import de.fhdw.wtf.common.exception.parser.AbstractParserException;
15  import de.fhdw.wtf.common.exception.parser.ClassModifierAlreadyAddedException;
16  import de.fhdw.wtf.common.exception.parser.NoSemicolonException;
17  import de.fhdw.wtf.common.stream.TokenStream;
18  import de.fhdw.wtf.common.token.Token;
19  
20  /**
21   * Parses a given Tokenstream to {@link ClassModifier}s ( {@link ClassModifierAbstract}, {@link ClassModifierTransient},
22   * {@link ClassModifierService}).
23   */
24  public final class ClassModifierParser {
25  	/**
26  	 * Tokenstream.
27  	 */
28  	private final TokenStream stream;
29  	/**
30  	 * Collection for exceptions.
31  	 */
32  	private final Collection<AbstractParserException> exceptions;
33  	
34  	/**
35  	 * Constrcutor of {@link ClassModifierParser}.
36  	 * 
37  	 * @param stream
38  	 *            tokenstream
39  	 * @param exceptions
40  	 *            collection for exceptions
41  	 */
42  	private ClassModifierParser(final TokenStream stream, final Collection<AbstractParserException> exceptions) {
43  		this.stream = stream;
44  		this.exceptions = exceptions;
45  	}
46  	
47  	/**
48  	 * Creates a {@link ClassModifierParser}-Object.
49  	 * 
50  	 * @param stream
51  	 *            tokenstream
52  	 * @param exceptions
53  	 *            collection for exceptions
54  	 * @return the {@link ClassModifierParser}-Object.
55  	 */
56  	static ClassModifierParser create(final TokenStream stream, final Collection<AbstractParserException> exceptions) {
57  		return new ClassModifierParser(stream, exceptions);
58  	}
59  	
60  	/**
61  	 * Parses the next series of Tokens of this.stream to ClassModifiers until a SemicolonToken occurs. If one of the
62  	 * Tokens between the first element of this.stream and the next SemicolonToken is not a subtype of
63  	 * ClassModifierToken, a {@link NoSemicolonException} will be thrown.
64  	 * 
65  	 * @param currentClass
66  	 *            class
67  	 * @throws AbstractParserException
68  	 *             {@link NoSemicolonException}
69  	 * @return modifiers
70  	 */
71  	Collection<ClassModifier> parse(final ClassType currentClass) throws AbstractParserException {
72  		final Collection<ClassModifier> result = new ArrayList<>();
73  		while (this.stream.hasNext()) {
74  			if (this.stream.peek().isSemicolonToken()) {
75  				this.stream.next();
76  				currentClass.setLastToken(this.stream.peek());
77  				break;
78  			} else {
79  				try {
80  					result.add(this.parseClassModifier(result));
81  				} catch (final ClassModifierAlreadyAddedException e3) {
82  					this.stream.removeFirst();
83  					this.exceptions.add(e3);
84  				}
85  			}
86  		}
87  		return result;
88  	}
89  	
90  	/**
91  	 * Checks if the next Token of this.stream is a ClassModifierToken and returns it, if it is. Otherwise a
92  	 * NoClassModifierException will be returned.
93  	 * 
94  	 * @param modifiers
95  	 *            list of modifiers
96  	 * @throws ClassModifierAlreadyAddedException
97  	 *             ClassModifierAlreadyAddedException
98  	 * @throws NoSemicolonException
99  	 *             NoSemicolonException
100 	 * @return ClassModifier
101 	 */
102 	private ClassModifier parseClassModifier(final Collection<ClassModifier> modifiers) throws NoSemicolonException,
103 			ClassModifierAlreadyAddedException {
104 		final Token nextToken = this.stream.peek();
105 		if (nextToken.isClassModifierToken()) {
106 			if (containsClassModifier(modifiers, nextToken)) {
107 				throw ClassModifierAlreadyAddedException.create(nextToken);
108 			}
109 			if (nextToken.isAbstractModifierToken()) {
110 				this.stream.removeFirst();
111 				return ClassModifierAbstract.create(nextToken, this.stream.peek());
112 			}
113 			if (nextToken.isServiceToken()) {
114 				this.stream.removeFirst();
115 				return ClassModifierService.create(nextToken, this.stream.peek());
116 			}
117 			if (nextToken.isTransientToken()) {
118 				this.stream.removeFirst();
119 				return ClassModifierTransient.create(nextToken, this.stream.peek());
120 			}
121 			if (nextToken.isVisitableToken()) {
122 				this.stream.removeFirst();
123 				return ClassModifierVisitable.create(nextToken, this.stream.peek());
124 			}
125 		}
126 		throw NoSemicolonException.create(nextToken);
127 	}
128 	
129 	/**
130 	 * Checks if <code>modifiers</code> contains a modifier of the type of <code>modifier</code>.
131 	 * 
132 	 * @param modifiers
133 	 *            : Collection to be searched in
134 	 * 
135 	 * @param modifier
136 	 *            : Modifiers of {@link ClassModifier} to search for
137 	 * @return : <true> if <code>modifiers</code> contains <code>modifier</code> , <false> otherwise
138 	 */
139 	private static boolean containsClassModifier(final Collection<ClassModifier> modifiers, final Token modifier) {
140 		final Iterator<ClassModifier> i = modifiers.iterator();
141 		while (i.hasNext()) {
142 			final ClassModifier current = i.next();
143 			if (current.accept(new ClassModifierVisitor() {
144 				@Override
145 				public boolean handle(final ClassModifierAbstract abstract1) {
146 					return modifier.isAbstractModifierToken();
147 				}
148 				
149 				@Override
150 				public boolean handle(final ClassModifierTransient tranzient) {
151 					return modifier.isTransientToken();
152 				}
153 				
154 				@Override
155 				public boolean handle(final ClassModifierService service) {
156 					return modifier.isServiceToken();
157 				}
158 				
159 				@Override
160 				public boolean handle(final ClassModifierVisitable visitable) {
161 					return modifier.isVisitableToken();
162 				}
163 			})) {
164 				return true;
165 			}
166 		}
167 		return false;
168 	}
169 }