View Javadoc

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 }