/plugins/JavaSideKick/tags/javasidekick-2-7-0/src/sidekick/property/parser/property/PropertyParser.jj

# · Unknown · 302 lines · 256 code · 46 blank · 0 comment · 0 complexity · 54b8ebd8373da531361e3df1db72ad42 MD5 · raw file

  1. /*
  2. Copyright (c) 2006, Dale Anson
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without modification,
  5. are permitted provided that the following conditions are met:
  6. * Redistributions of source code must retain the above copyright notice,
  7. this list of conditions and the following disclaimer.
  8. * Redistributions in binary form must reproduce the above copyright notice,
  9. this list of conditions and the following disclaimer in the documentation
  10. and/or other materials provided with the distribution.
  11. * Neither the name of the <ORGANIZATION> nor the names of its contributors
  12. may be used to endorse or promote products derived from this software without
  13. specific prior written permission.
  14. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  15. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  16. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  17. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  18. ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  19. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  20. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  21. ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  23. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. /*
  26. Simple parser to read Java property files. See Sun's javadoc for
  27. java.util.Properties.load() for the specification of a property file,
  28. that's what I used as the rules for this parser.
  29. */
  30. options {
  31. JAVA_UNICODE_ESCAPE = true;
  32. STATIC = false;
  33. }
  34. PARSER_BEGIN(PropertyParser)
  35. package sidekick.property.parser.property;
  36. import java.util.*;
  37. import sidekick.util.Location;
  38. public class PropertyParser {
  39. // accumulates parse exceptions
  40. private List<ParseException> exceptions = new ArrayList<ParseException>();
  41. // for testing...
  42. public static void main(String args[]) throws ParseException {
  43. PropertyParser parser = new PropertyParser(System.in);
  44. parser.Properties();
  45. }
  46. /**
  47. * Utility to trim horizontal whitespace from the front of a string.
  48. * @param the string to trim
  49. * @return the trimmed string
  50. */
  51. public String trimFront(String s) {
  52. if (s == null || s.length() == 0) {
  53. return s;
  54. }
  55. int index = 0;
  56. for (int i = 0; i < s.length(); i++) {
  57. if (s.charAt(i) == ' ' || s.charAt(i) == '\t')
  58. ++index;
  59. else
  60. break;
  61. }
  62. return s.substring(index);
  63. }
  64. /**
  65. * Setting the tab size makes the token locations more accurate.
  66. * @param size the size of the tabs in the current jEdit buffer
  67. */
  68. public void setTabSize(int size) {
  69. jj_input_stream.setTabSize(size);
  70. }
  71. /**
  72. * @return the current tab size that the parser is using
  73. */
  74. public int getTabSize() {
  75. return jj_input_stream.getTabSize(0);
  76. }
  77. /**
  78. * Creates a start location from the given token.
  79. * @param t a token
  80. * @return the start location of the token.
  81. */
  82. public Location createStartLocation(Token t) {
  83. if (t == null)
  84. return new Location(0, 0);
  85. return new Location(t.beginLine, t.beginColumn);
  86. }
  87. /**
  88. * Creates an end location from the given token.
  89. * @param t a token
  90. * @return the end location of the token.
  91. */
  92. public Location createEndLocation(Token t) {
  93. if (t == null)
  94. return new Location(0, 0);
  95. return new Location(t.endLine, t.endColumn);
  96. }
  97. /**
  98. * Add an exception to the list of exceptions. Rather than failing on
  99. * any exception, this parser will continue parsing and will accumulate
  100. * any/all exceptions.
  101. * @param pe a ParseException to accumulate
  102. */
  103. public void addException(ParseException pe) {
  104. if (pe != null)
  105. exceptions.add(pe);
  106. }
  107. /**
  108. * @return the list of accumulated ParseExceptions
  109. */
  110. public List<ParseException> getExceptions() {
  111. return exceptions;
  112. }
  113. }
  114. PARSER_END(PropertyParser)
  115. /* skip blank lines and continuation characters */
  116. SKIP:
  117. {
  118. <NEWLINE: (["\r", "\n"])+ >
  119. |
  120. <BLANK_LINE: ([" ", "\t"])+(<NEWLINE>) >
  121. |
  122. <CONTINUATION: ("\\")(<NEWLINE>) >
  123. }
  124. <DEFAULT> TOKEN :
  125. {
  126. /* comments are single line only. They start with # or ! as the first non-
  127. whitespace character and extend to the end of the line. Comments cannot
  128. go at the end of a key/value line -- they will be treated as part of the
  129. value, NOT as a comment! */
  130. <COMMENT: ([" ", "\t"])*("#" | "!")(~["\r", "\n"])*("\n"|"\r"|"\r\n")? > : DEFAULT
  131. |
  132. /* a key with no value is assumed to have an empty value */
  133. <BARE_KEY: (([" ", "\t"])*((("\\") ( ":" | "=" | " " | "\t" | "#")) | (~[" ", "\t", "=", ":", "#"]))+ (<NEWLINE>)) > : DEFAULT
  134. |
  135. /* a key actually starts with the first non-whitespace character on a line and
  136. extends through the last character before the first horizontal whitespace
  137. character or : or =. This means that a key cannot contain any whitespace
  138. characters. Note that a key can have escaped space and tab and : and = as
  139. part of the key name. */
  140. <KEY: ([" ", "\t"])*((("\\") ( ":" | "=" | " " | "\t" | "#")) | (~[" ", "\t", "=", ":", "#"]))+ > : ParseEquals
  141. |
  142. <OTHER: ~[] >
  143. }
  144. <ParseEquals> TOKEN :
  145. {
  146. /* the equals may be horizontal whitespace or = or :. There can be horizontal
  147. whitespace before and after the = and :. I'm not worrying about the
  148. trailing whitespace here, that gets captured by the VALUE regex and is
  149. trimmed in the bnf production below. */
  150. <EQUALS: ([" ", "\t"])*("=") | ([" ", "\t"])*(":") | ([" ", "\t"])+ > : ParseValue
  151. }
  152. <ParseValue> TOKEN:
  153. {
  154. /* the value actually starts with the first non-horizontal whitespace character
  155. following the EQUALS, but this regex will also capture the horizontal whitespace
  156. following the EQUALS. This whitespace is trimmed in the bnf production below.
  157. The value can span several lines if the continuation character is the last
  158. character on the line. This needs work -- the value can have escaped \
  159. characters, and I'm not checking if that's the case. For example, if a line
  160. ends in \\, then it's an escaped \, not a continuation character, and should
  161. be part of the value. I think this is a rare use case, so I'm not going
  162. to worry about it until someone complains. */
  163. <VALUE: ((~["\r", "\n"])*(<CONTINUATION>)*)+(<NEWLINE>)? > : DEFAULT
  164. }
  165. List<Property> Properties() :
  166. {
  167. List<Property> list = new ArrayList<Property>();
  168. Property p = null;
  169. }
  170. {
  171. /* Property files are simple, there are either comments or properties,
  172. there is nothing else. */
  173. (
  174. LOOKAHEAD(2)
  175. (
  176. Comment()
  177. )
  178. |
  179. (
  180. p = Property()
  181. {
  182. if (p != null)
  183. list.add(p);
  184. }
  185. )
  186. )*
  187. <EOF>
  188. {
  189. /* always sort the list by property name -- could make the caller do
  190. this and just return the properties in the original order */
  191. Collections.sort(list);
  192. return list;
  193. }
  194. }
  195. /**
  196. * @return a single property from the property file.
  197. */
  198. Property Property() :
  199. {
  200. Token key = null;
  201. Token value = null;
  202. }
  203. {
  204. try {
  205. (
  206. LOOKAHEAD(2)
  207. (
  208. key=<KEY>
  209. {
  210. token_source.SwitchTo(ParseEquals);
  211. }
  212. <EQUALS>
  213. {
  214. token_source.SwitchTo(ParseValue);
  215. }
  216. value=<VALUE>
  217. )
  218. |
  219. (
  220. key=<BARE_KEY>
  221. {
  222. token_source.SwitchTo(DEFAULT);
  223. }
  224. )
  225. )
  226. {
  227. Property prop = new Property();
  228. prop.setStartLocation(createStartLocation(key));
  229. prop.setEndLocation(createEndLocation(value == null ? key : value));
  230. /* key -- need to trim as the regex production can capture whitespace,
  231. and by definition, the key cannot contain any whitespace characters */
  232. String out = key.image.trim();
  233. /* key can have escaped 'equals' characters, unescape them */
  234. out = out.replaceAll("\\\\=", "=");
  235. out = out.replaceAll("\\\\:", ":");
  236. out = out.replaceAll("\\\\ ", " ");
  237. out = out.replaceAll("\\\\\\t", "\t");
  238. prop.setKey(out);
  239. /* value -- need to combine multi-line values into a single line. Leading
  240. whitespace on continuation lines is discarded, as is leading whitespace
  241. at the start of the value. */
  242. out = value == null ? "" : value.image.replaceAll("\\\\(\\s)+", "");
  243. out = trimFront(out);
  244. prop.setValue(out);
  245. return prop;
  246. }
  247. } catch (ParseException e) {
  248. addException(generateParseException());
  249. }
  250. }
  251. /* For completeness only. This parser does nothing special with comments. */
  252. void Comment() :
  253. {}
  254. {
  255. <COMMENT>
  256. }