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 
17 package com.google.clearsilver.jsilver.data;
18 
19 import com.google.clearsilver.jsilver.resourceloader.ResourceLoader;
20 
21 import java.io.IOException;
22 import java.io.LineNumberReader;
23 import java.io.Reader;
24 import java.util.ArrayList;
25 import java.util.List;
26 
27 /**
28  * Parses data in HierachicalDataFormat (HDF), generating callbacks for data encountered in the
29  * stream.
30  */
31 public class DefaultHdfParser implements Parser {
32 
33   private int initialContextSize = 10;
34 
parse(Reader reader, Data output, ErrorHandler errorHandler, ResourceLoader resourceLoader, String dataFileName, boolean ignoreAttributes)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 
parseLine(String line, Data output, List<String> context, LineNumberReader lineReader, String dataFileName, ErrorHandler errorHandler)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 
stripComment(String line)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    */
readToToken(LineNumberReader reader, String token)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    */
createFullPath(List<String> context, String subPath)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    */
split(String line, String delimiter)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    */
newFactory()140   public static ParserFactory newFactory() {
141     return new ParserFactory() {
142       public Parser newInstance() {
143         return new DefaultHdfParser();
144       }
145     };
146   }
147 
148 }
149