View Javadoc

1   /*
2    * Zemucan: A Syntax Assistant for DB2
3    * Copyright (C) 2009, 2010 Andres Gomez Casanova
4    *
5    * This file is part of Zemucan.
6    *
7    * Zemucan is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as published by
9    * the Free Software Foundation; either version 3 of the License, or
10   * (at your option) any later version.
11   *
12   * Zemucan is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public License
18   * along with this library; if not, see <http://www.gnu.org/licenses/>.
19   *
20   * Contact:
21   * a n g o c a  at  y a h o o  dot  c o m
22   * Cra. 45 No 61 - 31, Bogota, Colombia.
23   *
24   * Author:   $LastChangedBy: angoca $:
25   * Date:     $LastChangedDate: 2011-03-06 23:10:20 -0500 (dom, 06 mar 2011) $:
26   * Revision: $LastChangedRevision: 1917 $:
27   * URL:      $HeadURL: https://zemucan.svn.sourceforge.net/svnroot/zemucan/branches/zemucan_v1/source-code/grammarReaderApi/src/main/java/name/angoca/zemucan/grammarReader/api/GrammarReaderController.java $:
28   */
29  package name.angoca.zemucan.grammarReader.api;
30  
31  import java.lang.reflect.Constructor;
32  import java.lang.reflect.InvocationTargetException;
33  import java.util.ArrayList;
34  import java.util.List;
35  import java.util.StringTokenizer;
36  
37  import name.angoca.zemucan.AbstractZemucanException;
38  import name.angoca.zemucan.core.graph.model.Graph;
39  import name.angoca.zemucan.core.graph.model.StartingNode;
40  import name.angoca.zemucan.tools.Constants;
41  import name.angoca.zemucan.tools.configurator.Configurator;
42  
43  import org.slf4j.Logger;
44  import org.slf4j.LoggerFactory;
45  
46  /**
47   * This class controls the read of the grammar, and the graph construction. The
48   * class loads the corresponding GrammarReading by reflection, using the
49   * property that indicates the name of the class to use as GrammarReader.
50   * <p>
51   * <b>Control Version</b>
52   * <p>
53   * <ul>
54   * <li>1.0.0 Class creation.</li>
55   * <li>1.0.1 Grammar filename separator.</li>
56   * <li>1.1.0 Retrieves the graph.</li>
57   * <li>1.2.0 Synchronization.</li>
58   * <li>1.3.0 Interface.</li>
59   * <li>1.3.1 Empty grammar name.</li>
60   * <li>1.3.2 Files names are just tokens.</li>
61   * <li>1.4.0 New structure.</li>
62   * <li>1.4.1 Replaces with the returned graph.</li>
63   * <li>1.4.2 Javadoc, extra nodes from configuration.</li>
64   * </ul>
65   *
66   * @author Andres Gomez Casanova <a
67   *         href="mailto:a n g o c a at y a h o o dot c o m" >(AngocA)</a>
68   * @version 1.4.2 2010-08-08
69   * @since 1.0
70   */
71  public final class GrammarReaderController implements
72  		InterfaceGrammarReaderController {
73  	/**
74  	 * Unique instance of the grammar controller.
75  	 */
76  	private static GrammarReaderController instance;
77  	/**
78  	 * Logger.
79  	 */
80  	private static final Logger LOGGER = LoggerFactory
81  			.getLogger(GrammarReaderController.class);
82  
83  	/**
84  	 * Destroys the instance. Useful for testing purposes. It could be used when
85  	 * creating several graphs that are not connected.
86  	 */
87  	public static void destroyInstance() {
88  		GrammarReaderController.LOGGER
89  				.debug("Destroying GrammarReaderController instance."); //$NON-NLS-1$
90  
91  		if (GrammarReaderController.instance != null) {
92  			GrammarReaderController.instance = null;
93  		}
94  
95  		assert GrammarReaderController.instance == null;
96  	}
97  
98  	/**
99  	 * Retrieves the unique instance of the grammar controller. If it does not
100 	 * exist, it creates the corresponding instance. At that time the grammar is
101 	 * read.
102 	 * <p>
103 	 * This method is the implementation of the singleton pattern.
104 	 * <p>
105 	 * This method has a part where it is synchronized, however it is not thread
106 	 * safe because of the problem with the Single Pattern in Java
107 	 * (http://www.ibm.com/developerworks/java/library/j-dcl.html)
108 	 *
109 	 * @return The unique instance of grammar controller.
110 	 * @throws AbstractZemucanException
111 	 *             If the read graph is invalid. If a parameter is null when
112 	 *             calling a method. If the grammar is invalid. If there is a
113 	 *             problem calling the GrammarReader.
114 	 */
115 	public static GrammarReaderController/* ! */getInstance()
116 			throws AbstractZemucanException {
117 		if (GrammarReaderController.instance == null) {
118 			GrammarReaderController.LOGGER
119 					.debug("Creating GrammarReaderController instance"); //$NON-NLS-1$
120 			synchronized (GrammarReaderController.class) {
121 				GrammarReaderController.instance = new GrammarReaderController();
122 			}
123 		}
124 
125 		assert GrammarReaderController.instance != null;
126 		return GrammarReaderController.instance;
127 	}
128 
129 	/**
130 	 * Merge the read graphs in just one and establish the startingNode.
131 	 *
132 	 * @param graphs
133 	 *            set of graphs.
134 	 * @return The merge graph.
135 	 * @throws AbstractZemucanException
136 	 *             If there is a problem merging the graphs. If there is a
137 	 *             problem merging the graphs.
138 	 */
139 	public static Graph/* ! */mergeGraphs(final Graph[]/* [!]! */graphs)
140 			throws AbstractZemucanException {
141 		Graph masterGraph = null;
142 		if (graphs.length > 1) {
143 			masterGraph = graphs[0];
144 			for (int i = 1; i < graphs.length; i += 1) {
145 				final Graph graph = graphs[i];
146 				// Merge two graphs.
147 				graph.merge(masterGraph);
148 			}
149 			// Simplifies two ways that represent the same token.
150 			masterGraph.simplifyGraph();
151 		} else if (graphs.length == 1) {
152 			masterGraph = graphs[0];
153 		} else {
154 			assert false;
155 		}
156 
157 		return masterGraph;
158 	}
159 
160 	/**
161 	 * Retrieves all the globalDelimiters of all files that compose the grammar.
162 	 * The set has not repeat elements. TODO v1.1 revisar que los delimitadores
163 	 * de todos los archivos son los mismos, si no lo son, los grafos no se
164 	 * pueden navegar. TODO v2.0 en un futuro permitir gramaticas con diferentes
165 	 * delimitadores, pero solo se pueden interpretar grafos parciales, no toda
166 	 * la gramatica.
167 	 *
168 	 * @param delimiters
169 	 *            Set of globalDelimiters.
170 	 * @return Delimiters unified.
171 	 */
172 	public static String/* ! */processDelimiters(
173 			final List<String>/* [!]! */delimiters) {
174 		String globalDelimiters = ""; //$NON-NLS-1$
175 		for (final String delims : delimiters) {
176 			for (int j = 0; j < delims.length(); j += 1) {
177 				final char character = delims.charAt(j);
178 				if (globalDelimiters.indexOf(character) == -1) {
179 					globalDelimiters += delims.charAt(j);
180 				}
181 			}
182 		}
183 		GrammarReaderController.LOGGER
184 				.debug("Delimiters merged: " + globalDelimiters); //$NON-NLS-1$
185 		return globalDelimiters;
186 	}
187 
188 	/**
189 	 * Set of globalDelimiters of the grammar.
190 	 */
191 	private final String globalDelimiters;
192 
193 	/**
194 	 * Entry point of the graph.
195 	 */
196 	private final StartingNode startingNode;
197 
198 	/**
199 	 * Unified graph.
200 	 */
201 	private final Graph unifiedGgraph;
202 
203 	/**
204 	 * Creates the instance of the GrammarReader. It calls the corresponding
205 	 * GrammarReader class by its name doing reflection.
206 	 *
207 	 * @throws AbstractZemucanException
208 	 *             If the graph is invalid. If a parameter is null. If the
209 	 *             grammar is invalid. If there is a problem calling the
210 	 *             GrammarReader.
211 	 */
212 	private GrammarReaderController() throws AbstractZemucanException {
213 
214 		final Constructor<?> constructor = this.getConstructor();
215 
216 		// Retrieves the files that are part of the grammar
217 		final String grammarFileDescriptors = Configurator.getInstance()
218 				.getProperty(Constants.GRAMMAR_FILE_DESCRIPTORS_PROPERTY);
219 		final StringTokenizer tokenFileDescriptors = new StringTokenizer(
220 				grammarFileDescriptors, ";"); //$NON-NLS-1$
221 
222 		final int filesQty = tokenFileDescriptors.countTokens();
223 		final int position = 0;
224 		// Set of graph that represents each file of the grammar.
225 		final List<Graph> graphs = new ArrayList<Graph>(filesQty);
226 		final List<String> grammarDelimiters = new ArrayList<String>(filesQty);
227 		// Iterates over each file, creating a graph in each
228 		// iteration.
229 		while (tokenFileDescriptors.hasMoreTokens()) {
230 			final String fileDescriptor = tokenFileDescriptors.nextToken();
231 			this.readGraph(constructor, position, graphs, grammarDelimiters,
232 					fileDescriptor);
233 		}
234 		this.globalDelimiters = GrammarReaderController
235 				.processDelimiters(grammarDelimiters);
236 		this.unifiedGgraph = GrammarReaderController.mergeGraphs(graphs
237 				.toArray(new Graph[0]));
238 		this.startingNode = this.unifiedGgraph.getStartingNode();
239 		this.unifiedGgraph.checkDuplicates();
240 	}
241 
242 	/**
243 	 * Retrieves a constructor of a grammar reader by reflexivity of a class.
244 	 * The name of the class is obtained from the configurator.
245 	 *
246 	 * @return Constructor of the grammar reader.
247 	 * @throws GrammarReaderNotDefinedException
248 	 *             If there is not grammar reader defined in the configuration
249 	 *             file.
250 	 * @throws GeneralGrammarReaderProblemException
251 	 *             If there is a problem doing the reflexivity.
252 	 */
253 	private Constructor<?>/* ! */getConstructor()
254 			throws AbstractGrammarReaderException {
255 		// Retrieves the name of the grammar reader class.
256 		final String grammarReaderName = Configurator.getInstance()
257 				.getProperty(Constants.GRAMMAR_READER_NAME_PROPERTY);
258 		if ((grammarReaderName == null) || grammarReaderName.equals("")) {
259 			throw new GrammarReaderNotDefinedException();
260 		}
261 
262 		// Obtain the class by reflexivity.
263 		Class<?> clazz = null;
264 		try {
265 			clazz = Class.forName(grammarReaderName);
266 		} catch (final ClassNotFoundException e) {
267 			throw new GeneralGrammarReaderProblemException(e);
268 		}
269 		// Get the constructor of the class (Graph + String).
270 		Constructor<?> constructor = null;
271 		try {
272 			constructor = clazz.getConstructor(Graph.class, String.class);
273 		} catch (final NoSuchMethodException e) {
274 			throw new GeneralGrammarReaderProblemException(e);
275 		}
276 		return constructor;
277 	}
278 
279 	/*
280 	 * (non-Javadoc)
281 	 *
282 	 * @see
283 	 * name.angoca.zemucan.grammarReader.api.InterfaceGrammarReaderController
284 	 * #getDelimiters()
285 	 */
286 	@Override
287 	public String/* ! */getDelimiters() throws AbstractInvalidGrammarException {
288 		return this.globalDelimiters;
289 	}
290 
291 	/**
292 	 * Return the graph already unified.
293 	 *
294 	 * @return Unified graph.
295 	 */
296 	Graph/* ! */getGraph() {
297 		return this.unifiedGgraph;
298 	}
299 
300 	/*
301 	 * (non-Javadoc)
302 	 *
303 	 * @see
304 	 * name.angoca.zemucan.grammarReader.api.InterfaceGrammarReaderController
305 	 * #getStartingNode()
306 	 */
307 	@Override
308 	public StartingNode/* ! */getStartingNode()
309 			throws AbstractZemucanException {
310 		return this.startingNode;
311 	}
312 
313 	/**
314 	 * Reads a grammar file, and builds a graph, and then, it is stored in the
315 	 * given position.
316 	 *
317 	 * @param constructor
318 	 *            Grammar reader constructor.
319 	 * @param position
320 	 *            Position
321 	 * @param graphs
322 	 *            List of graphs to fill.
323 	 * @param grammarDelimiters
324 	 *            Grammar delimiter.
325 	 * @param fileDescriptor
326 	 *            Name of the file that contains the grammar.
327 	 * @return
328 	 * @throws AbstractZemucanException
329 	 */
330 	private int readGraph(final Constructor<?> constructor, int position,
331 			final List<Graph> graphs, final List<String> grammarDelimiters,
332 			final String fileDescriptor) throws AbstractZemucanException {
333 		final boolean extraNodes = !Boolean.parseBoolean(System
334 				.getProperty(Constants.WITHOUT_EXTRA_NODES));
335 		graphs.add(position, new Graph(fileDescriptor, extraNodes));
336 		try {
337 			//
338 			final Object grammar = constructor.newInstance(
339 					graphs.get(position), fileDescriptor);
340 			// Creates the graph
341 			final Graph newGraph = ((AbstractGrammarReader) grammar)
342 					.generateGraph();
343 			if (newGraph != graphs.get(position)) {
344 				graphs.remove(position);
345 				graphs.add(position, newGraph);
346 			}
347 			// Reads the graph.
348 			((AbstractGrammarReader) grammar).getStartingNode();
349 			// Reads the globalDelimiters of the graph in the
350 			// given file.
351 			grammarDelimiters.add(position,
352 					((AbstractGrammarReader) grammar).getDelimiters());
353 
354 			position += 1;
355 		} catch (final IllegalArgumentException e1) {
356 			// I don't know how to tests this part. It's out of my
357 			// scope.
358 			throw new GeneralGrammarReaderProblemException(e1);
359 		} catch (final InstantiationException e1) {
360 			throw new GeneralGrammarReaderProblemException(e1);
361 		} catch (final IllegalAccessException e1) {
362 			// I don't know how to tests this part. It's out of my
363 			// scope.
364 			throw new GeneralGrammarReaderProblemException(e1);
365 		} catch (final InvocationTargetException e1) {
366 			throw new GeneralGrammarReaderProblemException(e1);
367 		}
368 		return position;
369 	}
370 }