PageRenderTime 47ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/cayenne-3.0.1/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/jdbc/EJBQLTranslationContext.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 446 lines | 273 code | 84 blank | 89 comment | 40 complexity | d66b79225d592f41c8612f93d3851f34 MD5 | raw file
  1. /*****************************************************************
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. ****************************************************************/
  19. package org.apache.cayenne.access.jdbc;
  20. import java.util.ArrayList;
  21. import java.util.HashMap;
  22. import java.util.Iterator;
  23. import java.util.List;
  24. import java.util.Map;
  25. import org.apache.cayenne.ejbql.EJBQLCompiledExpression;
  26. import org.apache.cayenne.ejbql.EJBQLException;
  27. import org.apache.cayenne.map.DbEntity;
  28. import org.apache.cayenne.map.DbRelationship;
  29. import org.apache.cayenne.map.EntityResolver;
  30. import org.apache.cayenne.query.EJBQLQuery;
  31. import org.apache.cayenne.query.EntityResultSegment;
  32. import org.apache.cayenne.query.QueryMetadata;
  33. import org.apache.cayenne.query.SQLTemplate;
  34. import org.apache.cayenne.query.ScalarResultSegment;
  35. import org.apache.cayenne.reflect.ClassDescriptor;
  36. /**
  37. * A context used for translating of EJBQL to SQL.
  38. *
  39. * @since 3.0
  40. */
  41. public class EJBQLTranslationContext {
  42. private EJBQLCompiledExpression compiledExpression;
  43. protected Map<String, Object> namedParameters;
  44. protected Map<Integer, Object> positionalParameters;
  45. private EJBQLTranslatorFactory translatorFactory;
  46. private EntityResolver entityResolver;
  47. private List<Object> resultSetMetadata;
  48. private Map<String, String> tableAliases;
  49. private Map<String, Object> boundParameters;
  50. private Map<String, Object> attributes;
  51. private Map<String, String> idAliases;
  52. private int resultDescriptorPosition;
  53. private boolean usingAliases;
  54. private List<StringBuilder> bufferStack;
  55. private List<StringBuilder> bufferChain;
  56. private StringBuilder stackTop;
  57. private int subselectCount;
  58. private QueryMetadata queryMetadata;
  59. // a flag indicating whether column expressions should be treated as result columns or
  60. // not.
  61. private boolean appendingResultColumns;
  62. public EJBQLTranslationContext(EntityResolver entityResolver, EJBQLQuery query,
  63. EJBQLCompiledExpression compiledExpression,
  64. EJBQLTranslatorFactory translatorFactory) {
  65. this.entityResolver = entityResolver;
  66. this.compiledExpression = compiledExpression;
  67. this.resultSetMetadata = query.getMetaData(entityResolver).getResultSetMapping();
  68. this.namedParameters = query.getNamedParameters();
  69. this.positionalParameters = query.getPositionalParameters();
  70. this.translatorFactory = translatorFactory;
  71. this.usingAliases = true;
  72. this.queryMetadata = query.getMetaData(entityResolver);
  73. // buffer stack will hold named buffers during translation in the order they were
  74. // requested
  75. this.bufferStack = new ArrayList<StringBuilder>();
  76. // buffer chain will hold named and unnamed buffers in the order they should be
  77. // concatenated
  78. this.bufferChain = new ArrayList<StringBuilder>();
  79. stackTop = new StringBuilder();
  80. bufferChain.add(stackTop);
  81. bufferStack.add(stackTop);
  82. }
  83. SQLTemplate getQuery() {
  84. // concatenate buffers...
  85. StringBuilder main = bufferChain.get(0);
  86. for (int i = 1; i < bufferChain.size(); i++) {
  87. main.append(bufferChain.get(i));
  88. }
  89. String sql = main.length() > 0 ? main.toString() : null;
  90. SQLTemplate query = new SQLTemplate(compiledExpression
  91. .getRootDescriptor()
  92. .getObjectClass(), sql);
  93. query.setParameters(boundParameters);
  94. return query;
  95. }
  96. public QueryMetadata getMetadata(){
  97. return queryMetadata;
  98. }
  99. private String resolveId(String id) {
  100. if (idAliases == null) {
  101. return id;
  102. }
  103. String resolvedAlias = idAliases.get(id);
  104. if (resolvedAlias != null) {
  105. return resolvedAlias;
  106. }
  107. return id;
  108. }
  109. EJBQLTranslatorFactory getTranslatorFactory() {
  110. return translatorFactory;
  111. }
  112. EntityResolver getEntityResolver() {
  113. return entityResolver;
  114. }
  115. /**
  116. * Looks up entity descriptor for an identifier that can be a compiled expression id
  117. * or one of the aliases.
  118. */
  119. public ClassDescriptor getEntityDescriptor(String id) {
  120. return compiledExpression.getEntityDescriptor(resolveId(id));
  121. }
  122. List<DbRelationship> getIncomingRelationships(EJBQLTableId id) {
  123. List<DbRelationship> incoming = compiledExpression
  124. .getIncomingRelationships(resolveId(id.getEntityId()));
  125. // append tail of flattened relationships...
  126. if (id.getDbPath() != null) {
  127. DbEntity entity;
  128. if (incoming == null || incoming.isEmpty()) {
  129. entity = compiledExpression
  130. .getEntityDescriptor(id.getEntityId())
  131. .getEntity()
  132. .getDbEntity();
  133. }
  134. else {
  135. DbRelationship last = incoming.get(incoming.size() - 1);
  136. entity = (DbEntity) last.getTargetEntity();
  137. }
  138. incoming = new ArrayList<DbRelationship>(incoming);
  139. Iterator<?> it = entity.resolvePathComponents(id.getDbPath());
  140. while (it.hasNext()) {
  141. incoming.add((DbRelationship) it.next());
  142. }
  143. }
  144. return incoming;
  145. }
  146. /**
  147. * Creates a previously unused id alias for an entity identified by an id.
  148. */
  149. String createIdAlias(String id) {
  150. if (idAliases == null) {
  151. idAliases = new HashMap<String, String>();
  152. }
  153. for (int i = 0; i < 1000; i++) {
  154. String alias = id + "_alias" + i;
  155. if (idAliases.containsKey(alias)) {
  156. continue;
  157. }
  158. if (compiledExpression.getEntityDescriptor(alias) != null) {
  159. continue;
  160. }
  161. idAliases.put(alias, id);
  162. return alias;
  163. }
  164. throw new EJBQLException("Failed to create id alias");
  165. }
  166. /**
  167. * Inserts a marker in the SQL, mapped to a StringBuilder that can be later filled
  168. * with content.
  169. */
  170. void markCurrentPosition(String marker) {
  171. StringBuilder buffer = findOrCreateMarkedBuffer(marker);
  172. bufferChain.add(buffer);
  173. // immediately create unmarked buffer after the marked one and replace the bottom
  174. // of the stack with it
  175. StringBuilder tailBuffer = new StringBuilder();
  176. bufferChain.add(tailBuffer);
  177. bufferStack.set(0, tailBuffer);
  178. stackTop = bufferStack.get(bufferStack.size() - 1);
  179. }
  180. /**
  181. * Switches the current buffer to a marked buffer, pushing the currently used buffer
  182. * on the stack. Note that this can be done even before the marker is inserted in the
  183. * main buffer. If "reset" is true, any previous contents of the marker are cleared.
  184. */
  185. public void pushMarker(String marker, boolean reset) {
  186. stackTop = findOrCreateMarkedBuffer(marker);
  187. if (reset) {
  188. stackTop.delete(0, stackTop.length());
  189. }
  190. bufferStack.add(stackTop);
  191. }
  192. /**
  193. * Pops a marker stack, switching to the previously used marker.
  194. */
  195. void popMarker() {
  196. int lastIndex = bufferStack.size() - 1;
  197. bufferStack.remove(lastIndex);
  198. stackTop = bufferStack.get(lastIndex - 1);
  199. }
  200. StringBuilder findOrCreateMarkedBuffer(String marker) {
  201. StringBuilder buffer = (StringBuilder) getAttribute(marker);
  202. if (buffer == null) {
  203. buffer = new StringBuilder();
  204. // register mapping of internal to external marker
  205. setAttribute(marker, buffer);
  206. }
  207. return buffer;
  208. }
  209. /**
  210. * Returns a context "attribute" stored for the given name. Attributes is a state
  211. * preservation mechanism used by translators and have the same scope as the context.
  212. */
  213. Object getAttribute(String name) {
  214. return attributes != null ? attributes.get(name) : null;
  215. }
  216. /**
  217. * Sets a context "attribute". Attributes is a state preservation mechanism used by
  218. * translators and have the same scope as the context.
  219. */
  220. void setAttribute(String var, Object value) {
  221. if (attributes == null) {
  222. attributes = new HashMap<String, Object>();
  223. }
  224. attributes.put(var, value);
  225. }
  226. /**
  227. * Appends a piece of SQL to the internal buffer.
  228. */
  229. public EJBQLTranslationContext append(String chunk) {
  230. stackTop.append(chunk);
  231. return this;
  232. }
  233. /**
  234. * Appends a piece of SQL to the internal buffer.
  235. */
  236. public EJBQLTranslationContext append(char chunk) {
  237. stackTop.append(chunk);
  238. return this;
  239. }
  240. /**
  241. * Deletes a specified number of characters from the end of the current buffer.
  242. */
  243. EJBQLTranslationContext trim(int n) {
  244. int len = stackTop.length();
  245. if (len >= n) {
  246. stackTop.delete(len - n, len);
  247. }
  248. return this;
  249. }
  250. EJBQLCompiledExpression getCompiledExpression() {
  251. return compiledExpression;
  252. }
  253. String bindPositionalParameter(int position) {
  254. return bindParameter(positionalParameters.get(position));
  255. }
  256. String bindNamedParameter(String name) {
  257. return bindParameter(namedParameters.get(name));
  258. }
  259. /**
  260. * Creates a new parameter variable, binding provided value to it.
  261. */
  262. String bindParameter(Object value) {
  263. return bindParameter(value, "id");
  264. }
  265. void rebindParameter(String boundName, Object newValue) {
  266. boundParameters.put(boundName, newValue);
  267. }
  268. /**
  269. * Creates a new parameter variable with the specified prefix, binding provided value
  270. * to it.
  271. */
  272. String bindParameter(Object value, String prefix) {
  273. if (boundParameters == null) {
  274. boundParameters = new HashMap<String, Object>();
  275. }
  276. String var = prefix + boundParameters.size();
  277. boundParameters.put(var, value);
  278. return var;
  279. }
  280. Object getBoundParameter(String name) {
  281. return boundParameters != null ? boundParameters.get(name) : null;
  282. }
  283. /**
  284. * Retrieves a SQL alias for the combination of EJBQL id variable and a table name. If
  285. * such alias hasn't been used, it is created on the fly.
  286. */
  287. protected String getTableAlias(String idPath, String tableName) {
  288. if (!isUsingAliases()) {
  289. return tableName;
  290. }
  291. StringBuilder keyBuffer = new StringBuilder();
  292. // per JPA spec, 4.4.2, "Identification variables are case insensitive.", while
  293. // relationship path is case-sensitive
  294. int dot = idPath.indexOf('.');
  295. if (dot > 0) {
  296. keyBuffer.append(idPath.substring(0, dot).toLowerCase()).append(
  297. idPath.substring(dot));
  298. }
  299. else {
  300. keyBuffer.append(idPath.toLowerCase());
  301. }
  302. String key = keyBuffer.append(':').append(tableName).toString();
  303. String alias;
  304. if (tableAliases != null) {
  305. alias = tableAliases.get(key);
  306. }
  307. else {
  308. tableAliases = new HashMap<String, String>();
  309. alias = null;
  310. }
  311. if (alias == null) {
  312. alias = "t" + tableAliases.size();
  313. tableAliases.put(key, alias);
  314. }
  315. return alias;
  316. }
  317. /**
  318. * Returns a positional EntityResult, incrementing position index on each call.
  319. */
  320. EntityResultSegment nextEntityResult() {
  321. if (resultSetMetadata == null) {
  322. throw new EJBQLException(
  323. "No result set mapping exists for expression, can't map EntityResult");
  324. }
  325. return (EntityResultSegment) resultSetMetadata.get(resultDescriptorPosition++);
  326. }
  327. /**
  328. * Returns a positional column alias, incrementing position index on each call.
  329. */
  330. String nextColumnAlias() {
  331. if (resultSetMetadata == null) {
  332. throw new EJBQLException(
  333. "No result set mapping exists for expression, can't map column aliases");
  334. }
  335. return ((ScalarResultSegment) resultSetMetadata.get(resultDescriptorPosition++))
  336. .getColumn();
  337. }
  338. public boolean isAppendingResultColumns() {
  339. return appendingResultColumns;
  340. }
  341. void setAppendingResultColumns(boolean appendingResultColumns) {
  342. this.appendingResultColumns = appendingResultColumns;
  343. }
  344. public boolean isUsingAliases() {
  345. return usingAliases;
  346. }
  347. public void setUsingAliases(boolean useAliases) {
  348. this.usingAliases = useAliases;
  349. }
  350. public void onSubselect() {
  351. subselectCount++;
  352. }
  353. public String makeDistinctMarker() {
  354. return "DISTINCT_MARKER" + subselectCount;
  355. }
  356. String makeWhereMarker() {
  357. return "WHERE_MARKER" + subselectCount;
  358. }
  359. String makeEntityQualifierMarker() {
  360. return "ENTITY_QUALIIER" + subselectCount;
  361. }
  362. }