001    package org.hackystat.telemetry.analyzer.language.parser;
002    
003    import java.io.BufferedReader;
004    import java.io.StringReader;
005    import java.util.ArrayList;
006    import java.util.List;
007    
008    import org.hackystat.telemetry.analyzer.language.ast.TelemetryChartDefinition;
009    import org.hackystat.telemetry.analyzer.language.ast.TelemetryChartYAxisDefinition;
010    import org.hackystat.telemetry.analyzer.language.ast.TelemetryDefinition;
011    import org.hackystat.telemetry.analyzer.language.ast.TelemetryReportDefinition;
012    import org.hackystat.telemetry.analyzer.language.ast.TelemetryStreamsDefinition;
013    import org.hackystat.telemetry.analyzer.language.ast.TextPosition;
014    import org.hackystat.telemetry.analyzer.language.parser.impl.TelemetryLanguageParserImpl;
015    
016    /**
017     * Parser for telemetry language. All static methods in this class are thread-safe.
018     * 
019     * @author (Cedric) Qin Zhang
020     * @version $Id$
021     */
022    public class TelemetryLanguageParser {
023    
024      /**
025       * Private construct to prevent this class from being instantiated.
026       *
027       */
028      private TelemetryLanguageParser() {
029      }
030      
031      /**
032       * Parsing full telemetry streams and charts definitions.
033       * 
034       * @param string The definitions.
035       * @return A list containing <code>TelemetryDefinition</code> objects in the order appeared.
036       * 
037       * @throws ParsingException If there is parsing error.
038       */
039      @SuppressWarnings("unchecked")
040      public static List<TelemetryDefinition> parse(String string) throws ParsingException {
041        try {
042          TelemetryLanguageParserImpl parser 
043              = new TelemetryLanguageParserImpl(new StringReader(string));
044          List<TelemetryDefinition> defs = parser.all_input();
045          TextExtractor textExtractor = new TextExtractor(string);
046          for (TelemetryDefinition def : defs) {
047            def.setDefinitionString(textExtractor.extract(def.getTextPosition()));       
048          }
049          return defs;
050        }
051        catch (Throwable ex) { // might get JavaCC TokenMgrError
052          throw new ParsingException(ex);
053        }
054      }
055    
056      /**
057       * Parses telemetry "streams" object definition.
058       * 
059       * @param string A telemetry query statement that defines a telemetry "streams" object. 
060       *        Note that you should not include the final semi-colon.
061       *        
062       * @return Java object representation of the "streams" definition.
063       * 
064       * @throws ParsingException If the input is not grammatically correct.
065       */
066      public static TelemetryStreamsDefinition parseStreamsDef(String string) throws ParsingException {
067        try {
068          TelemetryLanguageParserImpl parser 
069              = new TelemetryLanguageParserImpl(new StringReader(string));
070          TelemetryStreamsDefinition def = parser.streams_statement_input();
071          def.setDefinitionString(new TextExtractor(string).extract(def.getTextPosition()));
072          return def;
073        }
074        catch (Throwable ex) { // might get JavaCC TokenMgrError
075          throw new ParsingException(ex);
076        }
077      }
078    
079      /**
080       * Parses telemetry "chart" object definition.
081       * 
082       * @param string A telemetry query statement that defines a telemetry "chart" object. 
083       *        Note that you should not include the final semi-colon.
084       *          
085       * @return Java object representation of the "chart" definition.
086       * 
087       * @throws ParsingException If the input is not gramatically correct.
088       */
089      public static TelemetryChartDefinition parseChartDef(String string) throws ParsingException {
090        try {
091          TelemetryLanguageParserImpl parser 
092              = new TelemetryLanguageParserImpl(new StringReader(string));
093          TelemetryChartDefinition def = parser.chart_statement_input();
094          def.setDefinitionString(new TextExtractor(string).extract(def.getTextPosition()));
095          return def;
096        }
097        catch (Throwable ex) { // might get JavaCC TokenMgrError
098          throw new ParsingException(ex);
099        }
100      }
101      
102      /**
103       * Parses telemetry "chart y-axis" object definition.
104       * 
105       * @param string A telemetry query statement that defines a telemetry "chart y-axis" object. 
106       *        Note that you should not include the final semi-colon.
107       *          
108       * @return Java object representation of the "chart y-axis" definition.
109       * 
110       * @throws ParsingException If the input is not gramatically correct.
111       */
112      public static TelemetryChartYAxisDefinition parseChartYAxisDef(String string) 
113          throws ParsingException {
114        try {
115          TelemetryLanguageParserImpl parser 
116              = new TelemetryLanguageParserImpl(new StringReader(string));
117          TelemetryChartYAxisDefinition def = parser.chart_y_axis_statement_input();
118          def.setDefinitionString(new TextExtractor(string).extract(def.getTextPosition()));
119          return def;
120        }
121        catch (Throwable ex) { // might get JavaCC TokenMgrError
122          throw new ParsingException(ex);
123        }
124      }
125    
126      /**
127       * Parses telemetry "report" object definition.
128       * 
129       * @param string A telemetry query statement that defines a telemetry "report" object. 
130       *        Note that you should not include the final semi-colon.
131       *          
132       * @return Java object representation of the "report" definition.
133       * 
134       * @throws ParsingException If the input is not gramatically correct.
135       */
136      public static TelemetryReportDefinition parseReportDef(String string) throws ParsingException {
137        try {
138          TelemetryLanguageParserImpl parser 
139              = new TelemetryLanguageParserImpl(new StringReader(string));
140          TelemetryReportDefinition def = parser.report_statement_input();
141          def.setDefinitionString(new TextExtractor(string).extract(def.getTextPosition()));
142          return def;
143        }
144        catch (Throwable ex) { // might get JavaCC TokenMgrError
145          throw new ParsingException(ex);
146        }
147      }
148      
149      /**
150       * Extracts text at given position.
151       * 
152       * @author (Cedric) Qin ZHANG
153       * @version $Id$
154       */
155      private static class TextExtractor {
156        
157        private List<String> lines = new ArrayList<String>(4);
158        
159        /**
160         * Constructs this instance.
161         * 
162         * @param input The input string.
163         * @throws Exception If the input string cannot be parsed into lines.
164         */
165        private TextExtractor(String input) throws Exception {
166          BufferedReader reader = new BufferedReader(new StringReader(input));
167          String line = reader.readLine();
168          while (line != null) {
169            this.lines.add(line);
170            line = reader.readLine();
171          }
172          reader.close();
173        }
174        
175        /**
176         * Extracts text at given position.
177         * 
178         * @param textPosition The position.
179         * @return The extracted text.
180         * @throws Exception If we cannot extract the text.
181         */
182        private String extract(TextPosition textPosition) throws Exception {
183          //Note: the index in textPosition is 1-indexed.
184          int startRow = textPosition.getBeginLine() - 1;
185          int startCol = textPosition.getBeginColumn() - 1; 
186          int endRow = textPosition.getEndLine() - 1;
187          int endCol = textPosition.getEndColumn() - 1;
188          
189          if (startRow < endRow) {
190            StringBuffer buffer = new StringBuffer(64);      
191            String line = this.lines.get(startRow);
192            buffer.append(line.substring(startCol)).append('\n');
193            for (int i = startRow + 1; i < endRow; i++) {
194              buffer.append(this.lines.get(i)).append('\n');
195            }
196            line = this.lines.get(endRow);
197            buffer.append(line.substring(0, endCol + 1));
198            return buffer.toString();
199          }
200          else if (startRow == endRow) {
201            if (startCol <= endCol) {
202              String line = this.lines.get(startRow);
203              return line.substring(startCol, endCol + 1);
204            }
205            else {
206              throw new Exception("Begin column is greater than end column.");
207            }
208          }
209          else {
210            throw new Exception("Begin line is greater than end line.");
211          }
212        }
213      }
214    }