PageRenderTime 50ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/eclipse-wtp-jpa-3.4.0/org.eclipse.persistence.jpa.jpql/src/org/eclipse/persistence/jpa/jpql/parser/AbstractPathExpression.java

#
Java | 377 lines | 178 code | 53 blank | 146 comment | 38 complexity | 8ec80173d443416dc18c054b82e8359b MD5 | raw file
  1. /*******************************************************************************
  2. * Copyright (c) 2006, 2012 Oracle and/or its affiliates. All rights reserved.
  3. * This program and the accompanying materials are made available under the
  4. * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
  5. * which accompanies this distribution.
  6. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
  7. * and the Eclipse Distribution License is available at
  8. * http://www.eclipse.org/org/documents/edl-v10.php.
  9. *
  10. * Contributors:
  11. * Oracle - initial API and implementation
  12. *
  13. ******************************************************************************/
  14. package org.eclipse.persistence.jpa.jpql.parser;
  15. import java.util.ArrayList;
  16. import java.util.Collection;
  17. import java.util.List;
  18. import org.eclipse.persistence.jpa.jpql.WordParser;
  19. import org.eclipse.persistence.jpa.jpql.util.iterator.CloneListIterator;
  20. import org.eclipse.persistence.jpa.jpql.util.iterator.IterableListIterator;
  21. /**
  22. * An identification variable followed by the navigation operator (.) and a state field or
  23. * association field is a path expression. The type of the path expression is the type computed as
  24. * the result of navigation; that is, the type of the state field or association field to which the
  25. * expression navigates.
  26. *
  27. * @see CollectionValuedPathExpression
  28. * @see IdentificationVariable
  29. *
  30. * @version 2.4
  31. * @since 2.3
  32. * @author Pascal Filion
  33. */
  34. @SuppressWarnings("nls")
  35. public abstract class AbstractPathExpression extends AbstractExpression {
  36. /**
  37. * Determines whether the path ends with a dot or not.
  38. */
  39. private Boolean endsWithDot;
  40. /**
  41. * The identification variable that starts the path expression, which can be a sample {@link
  42. * IdentificationVariable identification variable}, an {@link EntryExpression entry expression},
  43. * a {@link ValueExpression value expression} or a {@link KeyExpression key expression}.
  44. */
  45. private AbstractExpression identificationVariable;
  46. /**
  47. * The state field path in a ordered list of string segments.
  48. */
  49. private List<String> paths;
  50. /**
  51. * The cached number of segments representing the path expression.
  52. */
  53. private int pathSize;
  54. /**
  55. * Determines whether the path starts with a dot or not.
  56. */
  57. private boolean startsWithDot;
  58. /**
  59. * Determines whether the identification variable is virtual, meaning it's not part of the query
  60. * but is required for proper navigability.
  61. */
  62. private boolean virtualIdentificationVariable;
  63. /**
  64. * Creates a new <code>AbstractPathExpression</code>.
  65. *
  66. * @param parent The parent of this expression
  67. * @param expression The identification variable that was already parsed, which means the
  68. * beginning of the parsing should start with a dot
  69. */
  70. protected AbstractPathExpression(AbstractExpression parent, AbstractExpression expression) {
  71. super(parent);
  72. this.pathSize = -1;
  73. this.identificationVariable = expression;
  74. this.identificationVariable.setParent(this);
  75. }
  76. /**
  77. * Creates a new <code>AbstractPathExpression</code>.
  78. *
  79. * @param parent The parent of this expression
  80. * @param expression The identification variable that was already parsed, which means the
  81. * beginning of the parsing should start with a dot
  82. * @param paths The path expression that is following the identification variable
  83. */
  84. public AbstractPathExpression(AbstractExpression parent,
  85. AbstractExpression expression,
  86. String paths) {
  87. super(parent, paths);
  88. this.pathSize = -1;
  89. this.identificationVariable = expression;
  90. this.identificationVariable.setParent(this);
  91. }
  92. /**
  93. * Creates a new <code>AbstractPathExpression</code>.
  94. *
  95. * @param parent The parent of this expression
  96. * @param paths The path expression
  97. */
  98. protected AbstractPathExpression(AbstractExpression parent, String paths) {
  99. super(parent, paths);
  100. this.pathSize = -1;
  101. }
  102. /**
  103. * {@inheritDoc}
  104. */
  105. public void acceptChildren(ExpressionVisitor visitor) {
  106. getIdentificationVariable().accept(visitor);
  107. }
  108. /**
  109. * {@inheritDoc}
  110. */
  111. @Override
  112. protected void addChildrenTo(Collection<Expression> children) {
  113. checkPaths();
  114. children.add(identificationVariable);
  115. }
  116. /**
  117. * {@inheritDoc}
  118. */
  119. @Override
  120. protected final void addOrderedChildrenTo(List<Expression> children) {
  121. checkPaths();
  122. children.add(identificationVariable);
  123. children.add(buildStringExpression(getText()));
  124. }
  125. private void checkPaths() {
  126. if (paths == null) {
  127. paths = new ArrayList<String>();
  128. String text = getText();
  129. StringBuilder word = new StringBuilder();
  130. boolean hasDot = text.indexOf(DOT) > -1;
  131. if ((identificationVariable != null) && !virtualIdentificationVariable) {
  132. paths.add(identificationVariable.toParsedText());
  133. }
  134. // Extract each path from the word
  135. for (int index = 0, count = text.length(); index < count; index++) {
  136. char character = text.charAt(index);
  137. // Skip the first '.' so an empty path isn't added
  138. if ((index == 0) &&
  139. (character == DOT) &&
  140. (identificationVariable != null))
  141. {
  142. continue;
  143. }
  144. // Append the character and continue to the next character
  145. if (character != DOT) {
  146. word.append(character);
  147. continue;
  148. }
  149. if (hasDot && (identificationVariable == null)) {
  150. if (word.length() == 0) {
  151. identificationVariable = buildNullExpression();
  152. }
  153. else {
  154. identificationVariable = new IdentificationVariable(this, word.toString());
  155. }
  156. }
  157. paths.add(word.toString());
  158. word.delete(0, word.length());
  159. }
  160. if (identificationVariable == null) {
  161. if (hasDot) {
  162. identificationVariable = new IdentificationVariable(this, word.toString());
  163. }
  164. else {
  165. identificationVariable = buildNullExpression();
  166. }
  167. }
  168. if (word.length() > 0) {
  169. paths.add(word.toString());
  170. }
  171. }
  172. }
  173. /**
  174. * Determines whether the path ends with a dot or not.
  175. *
  176. * @return <code>true</code> if the path ends with a dot; <code>false</code> otherwise
  177. */
  178. public final boolean endsWithDot() {
  179. if (endsWithDot == null) {
  180. String text = getText();
  181. endsWithDot = text.charAt(text.length() - 1) == DOT;
  182. }
  183. return endsWithDot;
  184. }
  185. /**
  186. * Returns the identification variable that starts the path expression, which can be a sample
  187. * identification variable, a map value, map key or map entry expression.
  188. *
  189. * @return The root of the path expression
  190. */
  191. public final Expression getIdentificationVariable() {
  192. checkPaths();
  193. return identificationVariable;
  194. }
  195. /**
  196. * Returns the specified segment of the state field path.
  197. *
  198. * @param index The 0-based segment index
  199. * @return The specified segment
  200. */
  201. public final String getPath(int index) {
  202. checkPaths();
  203. return paths.get(index);
  204. }
  205. /**
  206. * Determines whether the identification variable was parsed.
  207. *
  208. * @return <code>true</code> the identification variable was parsed; <code>false</code> otherwise
  209. */
  210. public final boolean hasIdentificationVariable() {
  211. checkPaths();
  212. return !identificationVariable.isNull() &&
  213. !identificationVariable.isVirtual();
  214. }
  215. /**
  216. * Determines whether this identification variable is virtual, meaning it's not part of the query
  217. * but is required for proper navigability.
  218. *
  219. * @return <code>true</code> if this identification variable was virtually created to fully
  220. * qualify path expression; <code>false</code> if it was parsed
  221. */
  222. public final boolean hasVirtualIdentificationVariable() {
  223. return virtualIdentificationVariable;
  224. }
  225. /**
  226. * {@inheritDoc}
  227. */
  228. @Override
  229. protected final void parse(WordParser wordParser, boolean tolerant) {
  230. String word = getText();
  231. if (!hasIdentificationVariable()) {
  232. startsWithDot = word.startsWith(".");
  233. }
  234. // A null WordParser happens in a unique case
  235. wordParser.moveForward(word);
  236. }
  237. /**
  238. * Returns the segments in the state field path in order.
  239. *
  240. * @return An <code>Iterator</code> over the segments of the state field path
  241. */
  242. public final IterableListIterator<String> paths() {
  243. checkPaths();
  244. return new CloneListIterator<String>(paths);
  245. }
  246. /**
  247. * Returns the number of segments in the state field path.
  248. *
  249. * @return The number of segments
  250. */
  251. public final int pathSize() {
  252. if (pathSize == -1) {
  253. checkPaths();
  254. pathSize = paths.size();
  255. }
  256. return pathSize;
  257. }
  258. /**
  259. * Sets a virtual identification variable because the abstract schema name was parsed without
  260. * one. This is valid in an <b>UPDATE</b> and <b>DELETE</b> queries.
  261. *
  262. * @param variableName The identification variable that was generated to identify the abstract
  263. * schema name
  264. */
  265. protected final void setVirtualIdentificationVariable(String variableName) {
  266. paths = null;
  267. virtualIdentificationVariable = true;
  268. identificationVariable = new IdentificationVariable(this, variableName, true);
  269. rebuildActualText();
  270. rebuildParsedText();
  271. }
  272. /**
  273. * Determines whether the path starts with a dot or not.
  274. *
  275. * @return <code>true</code> if the path starts with a dot; <code>false</code> otherwise
  276. */
  277. public final boolean startsWithDot() {
  278. return startsWithDot;
  279. }
  280. /**
  281. * Returns a string representation from the given range.
  282. *
  283. * @param startIndex The beginning of the range to create the string representation
  284. * @param stopIndex When to stop creating the string representation, which is exclusive
  285. * @return The string representation of this path expression contained in the given range
  286. * @since 2.4
  287. */
  288. public String toParsedText(int startIndex, int stopIndex) {
  289. checkPaths();
  290. StringBuilder writer = new StringBuilder();
  291. for (int index = startIndex; index < stopIndex; index++) {
  292. writer.append(paths.get(index));
  293. if (index < stopIndex - 1) {
  294. writer.append(DOT);
  295. }
  296. }
  297. return writer.toString();
  298. }
  299. /**
  300. * {@inheritDoc}
  301. */
  302. @Override
  303. protected final void toParsedText(StringBuilder writer, boolean actual) {
  304. int pathSize = pathSize();
  305. if (startsWithDot) {
  306. writer.append(DOT);
  307. }
  308. else if (!virtualIdentificationVariable) {
  309. identificationVariable.toParsedText(writer, actual);
  310. if (pathSize > 1) {
  311. writer.append(DOT);
  312. }
  313. }
  314. for (int index = (virtualIdentificationVariable ? 0 : 1); index < pathSize; index++) {
  315. writer.append(paths.get(index));
  316. if (index < pathSize - 1) {
  317. writer.append(DOT);
  318. }
  319. }
  320. if (endsWithDot()) {
  321. writer.append(DOT);
  322. }
  323. }
  324. }