DefaultHdfParser.java revision 56ed4167b942ec265f9cee70ac4d71d10b3835ce
1/*
2 * Copyright (C) 2010 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.clearsilver.jsilver.data;
18
19import com.google.clearsilver.jsilver.resourceloader.ResourceLoader;
20
21import java.io.IOException;
22import java.io.LineNumberReader;
23import java.io.Reader;
24import java.util.ArrayList;
25import java.util.List;
26
27/**
28 * Parses data in HierachicalDataFormat (HDF), generating callbacks for data encountered in the
29 * stream.
30 */
31public class DefaultHdfParser implements Parser {
32
33  private int initialContextSize = 10;
34
35  public void parse(Reader reader, Data output, ErrorHandler errorHandler,
36      ResourceLoader resourceLoader, String dataFileName, boolean ignoreAttributes)
37      throws IOException {
38    LineNumberReader lineReader = new LineNumberReader(reader);
39    // Although a linked list could be used here, we iterate a lot and the
40    // size will rarely get > 10 deep. In this case ArrayList is faster than
41    // LinkedList.
42    List<String> context = new ArrayList<String>(initialContextSize);
43    String line;
44    while ((line = lineReader.readLine()) != null) {
45      parseLine(line, output, context, lineReader, dataFileName, errorHandler);
46    }
47  }
48
49  private void parseLine(String line, Data output, List<String> context,
50      LineNumberReader lineReader, String dataFileName, ErrorHandler errorHandler)
51      throws IOException {
52    line = stripComment(line);
53
54    Split split;
55    if ((split = split(line, "=")) != null) {
56      // some.thing = Hello
57      output.setValue(createFullPath(context, split.left), split.right);
58    } else if ((split = split(line, "<<")) != null) {
59      // some.thing << EOM
60      // Blah blah
61      // Blah blah
62      // EOM
63      output.setValue(createFullPath(context, split.left), readToToken(lineReader, split.right));
64    } else if ((split = split(line, "{")) != null) {
65      // some.thing {
66      // ...
67      context.add(split.left);
68    } else if (split(line, "}") != null) {
69      // ...
70      // }
71      context.remove(context.size() - 1);
72    } else if ((split = split(line, ":")) != null) {
73      // some.tree : another.tree
74      output.setSymlink(createFullPath(context, split.left), split.right);
75    } else if (line.trim().length() != 0) {
76      // Anything else
77      if (errorHandler != null) {
78        errorHandler.error(lineReader.getLineNumber(), line, dataFileName, "Bad HDF syntax");
79      }
80    }
81  }
82
83  private String stripComment(String line) {
84    int commentPosition = line.indexOf('#');
85    int equalsPosition = line.indexOf('=');
86    if (commentPosition > -1 && (equalsPosition == -1 || commentPosition < equalsPosition)) {
87      return line.substring(0, commentPosition);
88    } else {
89      return line;
90    }
91  }
92
93  /**
94   * Reads lines from a reader until a line is encountered that matches token (or end of stream).
95   */
96  private String readToToken(LineNumberReader reader, String token) throws IOException {
97    StringBuilder result = new StringBuilder();
98    String line;
99    while ((line = reader.readLine()) != null && !line.trim().equals(token)) {
100      result.append(line).append('\n');
101    }
102    return result.toString();
103  }
104
105  /**
106   * Creates the full path, based on the current context.
107   */
108  private String createFullPath(List<String> context, String subPath) {
109    StringBuilder result = new StringBuilder();
110    for (String contextItem : context) {
111      result.append(contextItem).append('.');
112    }
113    result.append(subPath);
114    return result.toString();
115  }
116
117  /**
118   * Split a line in two, based on a delimiter. If the delimiter is not found, null is returned.
119   */
120  private Split split(String line, String delimiter) {
121    int position = line.indexOf(delimiter);
122    if (position > -1) {
123      Split result = new Split();
124      result.left = line.substring(0, position).trim();
125      result.right = line.substring(position + delimiter.length()).trim();
126      return result;
127    } else {
128      return null;
129    }
130  }
131
132  private static class Split {
133    String left;
134    String right;
135  }
136
137  /**
138   * Returns a factory object that constructs DefaultHdfParser objects.
139   */
140  public static ParserFactory newFactory() {
141    return new ParserFactory() {
142      public Parser newInstance() {
143        return new DefaultHdfParser();
144      }
145    };
146  }
147
148}
149