Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

PropertyTokenizer.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /*
  2. * $Id$
  3. * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
  4. * For details on use and redistribution please refer to the
  5. * LICENSE file included with these sources.
  6. */
  7. package org.apache.fop.fo.expr;
  8. /**
  9. * Class to tokenize XSL FO property expression.
  10. * This class is heavily based on the epxression tokenizer in James Clark's
  11. * XT, an XSLT processor.
  12. */
  13. class PropertyTokenizer {
  14. static final int TOK_EOF = 0;
  15. static final int TOK_NCNAME = TOK_EOF + 1;
  16. static final int TOK_MULTIPLY = TOK_NCNAME + 1;
  17. static final int TOK_LPAR = TOK_MULTIPLY + 1;
  18. static final int TOK_RPAR = TOK_LPAR + 1;
  19. static final int TOK_LITERAL = TOK_RPAR + 1;
  20. static final int TOK_NUMBER = TOK_LITERAL + 1;
  21. static final int TOK_FUNCTION_LPAR = TOK_NUMBER + 1;
  22. static final int TOK_PLUS = TOK_FUNCTION_LPAR + 1;
  23. static final int TOK_MINUS = TOK_PLUS + 1;
  24. static final int TOK_MOD = TOK_MINUS + 1;
  25. static final int TOK_DIV = TOK_MOD + 1;
  26. static final int TOK_NUMERIC = TOK_DIV + 1;
  27. static final int TOK_COMMA = TOK_NUMERIC + 1;
  28. static final int TOK_PERCENT = TOK_COMMA + 1;
  29. static final int TOK_COLORSPEC = TOK_PERCENT + 1;
  30. static final int TOK_FLOAT = TOK_COLORSPEC + 1;
  31. static final int TOK_INTEGER = TOK_FLOAT + 1;
  32. int currentToken = TOK_EOF;
  33. String currentTokenValue = null;
  34. protected int currentUnitLength = 0;
  35. private int currentTokenStartIndex = 0;
  36. private /* final */ String expr;
  37. private int exprIndex = 0;
  38. private int exprLength;
  39. private boolean recognizeOperator = false;
  40. /**
  41. * Construct a new PropertyTokenizer object to tokenize the passed
  42. * String.
  43. * @param s The Property expressio to tokenize.
  44. */
  45. PropertyTokenizer(String s) {
  46. this.expr = s;
  47. this.exprLength = s.length();
  48. }
  49. /**
  50. * Return the next token in the expression string.
  51. * This sets the following package visible variables:
  52. * currentToken An enumerated value identifying the recognized token
  53. * currentTokenValue A String containing the token contents
  54. * currentUnitLength If currentToken = TOK_NUMERIC, the number of
  55. * characters in the unit name.
  56. * @throws PropertyException If un unrecognized token is encountered.
  57. */
  58. void next() throws PropertyException {
  59. currentTokenValue = null;
  60. currentTokenStartIndex = exprIndex;
  61. boolean currentMaybeOperator = recognizeOperator;
  62. boolean bSawDecimal;
  63. recognizeOperator = true;
  64. for (; ;) {
  65. if (exprIndex >= exprLength) {
  66. currentToken = TOK_EOF;
  67. return;
  68. }
  69. char c = expr.charAt(exprIndex++);
  70. switch (c) {
  71. case ' ':
  72. case '\t':
  73. case '\r':
  74. case '\n':
  75. currentTokenStartIndex = exprIndex;
  76. break;
  77. case ',':
  78. recognizeOperator = false;
  79. currentToken = TOK_COMMA;
  80. return;
  81. case '+':
  82. recognizeOperator = false;
  83. currentToken = TOK_PLUS;
  84. return;
  85. case '-':
  86. recognizeOperator = false;
  87. currentToken = TOK_MINUS;
  88. return;
  89. case '(':
  90. currentToken = TOK_LPAR;
  91. recognizeOperator = false;
  92. return;
  93. case ')':
  94. currentToken = TOK_RPAR;
  95. return;
  96. case '"':
  97. case '\'':
  98. exprIndex = expr.indexOf(c, exprIndex);
  99. if (exprIndex < 0) {
  100. exprIndex = currentTokenStartIndex + 1;
  101. throw new PropertyException("missing quote");
  102. }
  103. currentTokenValue = expr.substring(currentTokenStartIndex
  104. + 1, exprIndex++);
  105. currentToken = TOK_LITERAL;
  106. return;
  107. case '*':
  108. /*
  109. * if (currentMaybeOperator) {
  110. * recognizeOperator = false;
  111. */
  112. currentToken = TOK_MULTIPLY;
  113. /*
  114. * }
  115. * else
  116. * throw new PropertyException("illegal operator *");
  117. */
  118. return;
  119. case '0':
  120. case '1':
  121. case '2':
  122. case '3':
  123. case '4':
  124. case '5':
  125. case '6':
  126. case '7':
  127. case '8':
  128. case '9':
  129. scanDigits();
  130. if (exprIndex < exprLength && expr.charAt(exprIndex) == '.') {
  131. exprIndex++;
  132. bSawDecimal = true;
  133. if (exprIndex < exprLength
  134. && isDigit(expr.charAt(exprIndex))) {
  135. exprIndex++;
  136. scanDigits();
  137. }
  138. } else {
  139. bSawDecimal = false;
  140. }
  141. if (exprIndex < exprLength && expr.charAt(exprIndex) == '%') {
  142. exprIndex++;
  143. currentToken = TOK_PERCENT;
  144. } else {
  145. // Check for possible unit name following number
  146. currentUnitLength = exprIndex;
  147. scanName();
  148. currentUnitLength = exprIndex - currentUnitLength;
  149. currentToken = (currentUnitLength > 0) ? TOK_NUMERIC
  150. : (bSawDecimal ? TOK_FLOAT : TOK_INTEGER);
  151. }
  152. currentTokenValue = expr.substring(currentTokenStartIndex,
  153. exprIndex);
  154. return;
  155. case '.':
  156. if (exprIndex < exprLength
  157. && isDigit(expr.charAt(exprIndex))) {
  158. ++exprIndex;
  159. scanDigits();
  160. if (exprIndex < exprLength
  161. && expr.charAt(exprIndex) == '%') {
  162. exprIndex++;
  163. currentToken = TOK_PERCENT;
  164. } else {
  165. // Check for possible unit name following number
  166. currentUnitLength = exprIndex;
  167. scanName();
  168. currentUnitLength = exprIndex - currentUnitLength;
  169. currentToken = (currentUnitLength > 0) ? TOK_NUMERIC
  170. : TOK_FLOAT;
  171. }
  172. currentTokenValue = expr.substring(currentTokenStartIndex,
  173. exprIndex);
  174. return;
  175. }
  176. throw new PropertyException("illegal character '.'");
  177. case '#': // Start of color value
  178. if (exprIndex < exprLength
  179. && isHexDigit(expr.charAt(exprIndex))) {
  180. ++exprIndex;
  181. scanHexDigits();
  182. currentToken = TOK_COLORSPEC;
  183. currentTokenValue = expr.substring(currentTokenStartIndex,
  184. exprIndex);
  185. // Probably should have some multiple of 3 for length!
  186. return;
  187. } else {
  188. throw new PropertyException("illegal character '#'");
  189. }
  190. default:
  191. --exprIndex;
  192. scanName();
  193. if (exprIndex == currentTokenStartIndex) {
  194. throw new PropertyException("illegal character");
  195. }
  196. currentTokenValue = expr.substring(currentTokenStartIndex,
  197. exprIndex);
  198. // if (currentMaybeOperator) {
  199. if (currentTokenValue.equals("mod")) {
  200. currentToken = TOK_MOD;
  201. return;
  202. } else if (currentTokenValue.equals("div")) {
  203. currentToken = TOK_DIV;
  204. return;
  205. }
  206. /*
  207. * else
  208. * throw new PropertyException("unrecognized operator name");
  209. * recognizeOperator = false;
  210. * return;
  211. * }
  212. */
  213. if (followingParen()) {
  214. currentToken = TOK_FUNCTION_LPAR;
  215. recognizeOperator = false;
  216. } else {
  217. currentToken = TOK_NCNAME;
  218. recognizeOperator = false;
  219. }
  220. return;
  221. }
  222. }
  223. }
  224. /**
  225. * Attempt to recognize a valid NAME token in the input expression.
  226. */
  227. private void scanName() {
  228. if (exprIndex < exprLength && isNameStartChar(expr.charAt(exprIndex))) {
  229. while (++exprIndex < exprLength
  230. && isNameChar(expr.charAt(exprIndex))) { }
  231. }
  232. }
  233. /**
  234. * Attempt to recognize a valid sequence of decimal digits in the
  235. * input expression.
  236. */
  237. private void scanDigits() {
  238. while (exprIndex < exprLength && isDigit(expr.charAt(exprIndex))) {
  239. exprIndex++;
  240. }
  241. }
  242. /**
  243. * Attempt to recognize a valid sequence of hexadecimal digits in the
  244. * input expression.
  245. */
  246. private void scanHexDigits() {
  247. while (exprIndex < exprLength && isHexDigit(expr.charAt(exprIndex))) {
  248. exprIndex++;
  249. }
  250. }
  251. /**
  252. * Return a boolean value indicating whether the following non-whitespace
  253. * character is an opening parenthesis.
  254. */
  255. private boolean followingParen() {
  256. for (int i = exprIndex; i < exprLength; i++) {
  257. switch (expr.charAt(i)) {
  258. case '(':
  259. exprIndex = i + 1;
  260. return true;
  261. case ' ':
  262. case '\r':
  263. case '\n':
  264. case '\t':
  265. break;
  266. default:
  267. return false;
  268. }
  269. }
  270. return false;
  271. }
  272. private static final String nameStartChars =
  273. "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  274. private static final String nameChars = ".-0123456789";
  275. private static final String digits = "0123456789";
  276. private static final String hexchars = digits + "abcdefABCDEF";
  277. /**
  278. * Return a boolean value indicating whether the argument is a
  279. * decimal digit (0-9).
  280. * @param c The character to check
  281. */
  282. private static final boolean isDigit(char c) {
  283. return digits.indexOf(c) >= 0;
  284. }
  285. /**
  286. * Return a boolean value indicating whether the argument is a
  287. * hexadecimal digit (0-9, A-F, a-f).
  288. * @param c The character to check
  289. */
  290. private static final boolean isHexDigit(char c) {
  291. return hexchars.indexOf(c) >= 0;
  292. }
  293. /**
  294. * Return a boolean value indicating whether the argument is whitespace
  295. * as defined by XSL (space, newline, CR, tab).
  296. * @param c The character to check
  297. */
  298. private static final boolean isSpace(char c) {
  299. switch (c) {
  300. case ' ':
  301. case '\r':
  302. case '\n':
  303. case '\t':
  304. return true;
  305. }
  306. return false;
  307. }
  308. /**
  309. * Return a boolean value indicating whether the argument is a valid name
  310. * start character, ie. can start a NAME as defined by XSL.
  311. * @param c The character to check
  312. */
  313. private static final boolean isNameStartChar(char c) {
  314. return nameStartChars.indexOf(c) >= 0 || c >= 0x80;
  315. }
  316. /**
  317. * Return a boolean value indicating whether the argument is a valid name
  318. * character, ie. can occur in a NAME as defined by XSL.
  319. * @param c The character to check
  320. */
  321. private static final boolean isNameChar(char c) {
  322. return nameStartChars.indexOf(c) >= 0 || nameChars.indexOf(c) >= 0
  323. || c >= 0x80;
  324. }
  325. }