1 /*
2 * db2sa: DB2 Syntax Assistant
3 * Copyright (C) Andres Gomez Casanova
4 *
5 * This file is part of db2sa.
6 *
7 * db2sa 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 * db2sa 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: 2009-07-08 19:56:43 +0200 (Wed, 08 Jul 2009) $:
26 * Revision: $LastChangedRevision: 357 $:
27 * URL: $HeadURL: https://db2sa.svn.sourceforge.net/svnroot/db2sa/branches/db2sa_beta/source-code/src/main/java/name/angoca/db2sa/core/lexical/ImplLexicalAnalyzer.java $:
28 */
29 package name.angoca.db2sa.core.lexical;
30
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.StringTokenizer;
34
35 import name.angoca.db2sa.Configurator;
36 import name.angoca.db2sa.Constants;
37 import name.angoca.db2sa.core.ReturnOptions;
38 import name.angoca.db2sa.core.lexical.exceptions.InvalidTokenException;
39 import name.angoca.db2sa.core.syntax.GraphAnswer;
40 import name.angoca.db2sa.core.syntax.ImplSyntaxAnalyzer;
41 import name.angoca.db2sa.core.syntax.graph.exception.InvalidGraphException;
42
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 /**
47 * This is the implementation of the lexical analyzer.<br/>
48 * <b>Control Version</b><br />
49 * <ul>
50 * <li>0.0.1 Class creation.</li>
51 * <li>0.1.0</li>
52 * <li>0.2.0</li>
53 * <li>0.3.0 Recommendations from PMD.</li>
54 * <li>0.3.1 Organized.</li>
55 * <li>0.4.0 Invalid Graph Exception.</li>
56 * <li>0.5.0 Destroy instance.</li>
57 * <li>0.5.1 Logger messages.</li>
58 * <li>0.6.0 Delimiters not set in a static way.</li>
59 * <li>1.0.0 Moved to version 1.</li>
60 * </ul>
61 *
62 * @author Andres Gomez Casanova <a
63 * href="mailto:a n g o c a at y a h o o dot c o m">(AngocA)</a>
64 * @version 1.0.0 2009-07-19
65 */
66 public final class ImplLexicalAnalyzer extends AbstractLexicalAnalyzer {
67
68 /**
69 * Logger.
70 */
71 private static final Logger LOGGER = LoggerFactory
72 .getLogger(ImplLexicalAnalyzer.class);
73
74 /**
75 * The only instance of this object.
76 */
77 private static ImplLexicalAnalyzer s_instance;
78
79 /**
80 * Set of delimiters.
81 */
82 private final String m_delimiters;
83
84 /**
85 * Constructor that defines the delimiters of the tokens.
86 *
87 * @throws InvalidGraphException
88 * When there is a problem when creating the graph.
89 */
90 private ImplLexicalAnalyzer() throws InvalidGraphException {
91 super();
92 this.m_delimiters = Configurator.getInstance().getProperty(
93 Constants.DELIMITERS);
94 // This helps to run the processPhrase method faster the first time.
95 ImplSyntaxAnalyzer.getInstance();
96 }
97
98 /**
99 * Creates the only possible instance of this object. This method can be
100 * called after defining the delimiters for this object.
101 *
102 * @return This object instanced.
103 * @throws InvalidGraphException
104 * When there is a problem creating the graph.
105 */
106 public static ImplLexicalAnalyzer/* ! */getInstance()
107 throws InvalidGraphException {
108 // TODO revisar el objeto con lo que se hace el synchronized
109 synchronized (ImplLexicalAnalyzer.class) {
110 if (ImplLexicalAnalyzer.s_instance == null) {
111 ImplLexicalAnalyzer.LOGGER
112 .debug("Creating ImplLexicalAnalyzer instance"); //$NON-NLS-1$
113 ImplLexicalAnalyzer.s_instance = new ImplLexicalAnalyzer();
114 }
115 }
116 return ImplLexicalAnalyzer.s_instance;
117 }
118
119 /**
120 * Destroys the instance. It is useful for testings purposes.
121 */
122 public static void destroyInstance() {
123 ImplLexicalAnalyzer.LOGGER
124 .debug("Destroying ImplLexicalAnalyzer instance"); //$NON-NLS-1$
125
126 ImplSyntaxAnalyzer.destroyInstance();
127 ImplLexicalAnalyzer.s_instance = null;
128 }
129
130 /*
131 * (non-Javadoc)
132 *
133 * @see
134 * name.angoca.db2sa.core.lexical.LexicalAnalyzer#processPhrase(java.lang
135 * .String)
136 */
137 @Override
138 public ReturnOptions/* ? */processPhrase(final String/* ! */phrase)
139 throws InvalidGraphException {
140 // TODO reducir el cyclomatic complexity.
141 if (ImplLexicalAnalyzer.LOGGER.isDebugEnabled()) {
142 ImplLexicalAnalyzer.LOGGER.debug(
143 "Entering to lexical analyzer: '{}'", //$NON-NLS-1$
144 phrase);
145 }
146
147 // Delete all the unnecessary spaces.
148 String newPhrase = phrase.trim();
149
150 final StringTokenizer tokenizer = new StringTokenizer(newPhrase,
151 this.m_delimiters, true);
152
153 final List<Token> tokens = new ArrayList<Token>(tokenizer.countTokens());
154 String currentToken;
155 try {
156 // Scan the tokens and gets only the non space tokens.
157 // TODO Para no estar instanciando en el ciclo, contar la cantidad
158 // de espacios, y crear tantos token como espacios más uno.
159 while (tokenizer.hasMoreTokens()) {
160 currentToken = tokenizer.nextToken();
161 if (!(currentToken.equals(" "))) { //$NON-NLS-1$
162 final Token token = new Token(currentToken);
163 tokens.add(token);
164 ImplLexicalAnalyzer.LOGGER.debug(
165 "Token: {{}}", currentToken); //$NON-NLS-1$
166 }
167 }
168 } catch (InvalidTokenException e) {
169 // TODO Auto-generated catch block
170 ImplLexicalAnalyzer.LOGGER.error(e.getMessage());
171 }
172
173 final GraphAnswer answer = ImplSyntaxAnalyzer.getInstance().getOptions(
174 tokens);
175
176 // TODO Test para la reconversión de tokens a cadena 'create tab' ->
177 // 'create table'
178 // Replace the last token of the given phrase, with the last token
179 // returned by the grammatical analyzer.
180 // FIXME esto ya no es válido, el arreglo devuelto es el conjunto de
181 // opciones de la posición actual.
182 newPhrase = this.replaceLastToken(newPhrase, tokens, answer);
183
184 final int sizePhrases = answer.getPhrases().size();
185 final int sizeOptions = answer.getOptions().size();
186 String[] setOfPhrases = new String[sizePhrases];
187 if (sizePhrases == 1 && sizeOptions == 0) {
188 setOfPhrases = new String[] {};
189 } else {
190 for (int j = 0; j < sizePhrases; j += 1) {
191 final Token token = answer.getPhrases().get(j);
192 setOfPhrases[j] = token.getToken();
193 }
194
195 }
196 final String[] setOfOptions = new String[sizeOptions];
197 for (int j = 0; j < sizeOptions; j += 1) {
198 final Token token = answer.getOptions().get(j);
199 setOfOptions[j] = token.getToken();
200 }
201
202 if (ImplLexicalAnalyzer.LOGGER.isDebugEnabled()) {
203 ImplLexicalAnalyzer.LOGGER.debug("{} : {}", //$NON-NLS-1$
204 answer.getPhrases().toString(), answer.getOptions()
205 .toString());
206 }
207
208 // Creates an object that represents the command completed or the
209 // options.
210 final ReturnOptions ret = new ReturnOptions(newPhrase, setOfPhrases,
211 setOfOptions);
212 return ret;
213 }
214
215 /**
216 * Retrieves a phrase with the last token modified. The modification depends
217 * on the answer of the grammatical analyzer.
218 *
219 * @param phrase
220 * Original phrase.
221 * @param tokens
222 * Set of token that represents the original phrase.
223 * @param answer
224 * New phrase returned by the grammatical analyzer.
225 * @return a phrase with the last token modified according to the answer.
226 */
227 protected String replaceLastToken(final String phrase,
228 final List<Token> tokens, final GraphAnswer answer) {
229 String newPhrase = phrase;
230 final int lastIndexPhrase = tokens.size() - 1;
231
232 if (answer.getOptions().size() == 0 && answer.getPhrases().size() == 1
233 && lastIndexPhrase >= 0) {
234 // Retrieves the last token of the given phrase and delete it.
235 final String lastTokenPhrase = tokens.get(lastIndexPhrase)
236 .getToken();
237 final int lastToken = phrase.lastIndexOf(lastTokenPhrase);
238 newPhrase = phrase.substring(0, lastToken);
239
240 final String lastTokenGraph = answer.getPhrases().get(0).getToken();
241 newPhrase = newPhrase.concat(lastTokenGraph);
242 }
243 return newPhrase;
244 }
245 }