Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPascal Filion2012-10-11 17:24:39 +0000
committerPascal Filion2012-10-11 17:24:39 +0000
commit4a46daa57150ea5918ae27d6ac583cd7dfd0e8b3 (patch)
treeb94f72909d23176bfbb9d4e6dafa7732b0d3a41a /jpa/plugins
parent6c201b8fee970f429c8c1a1b3aba99f8b4e2bd15 (diff)
downloadwebtools.dali-4a46daa57150ea5918ae27d6ac583cd7dfd0e8b3.tar.gz
webtools.dali-4a46daa57150ea5918ae27d6ac583cd7dfd0e8b3.tar.xz
webtools.dali-4a46daa57150ea5918ae27d6ac583cd7dfd0e8b3.zip
Fix misalignment of text range within a JPQL query and incorrect behavior of content assist due to incorrect location within the query (fix bug#360713, bug#386414)
Diffstat (limited to 'jpa/plugins')
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/context/Query.java15
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/java/AbstractJavaQuery.java8
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/orm/AbstractOrmQuery.java24
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa1/context/java/GenericJavaNamedQuery.java12
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa1/context/orm/GenericOrmNamedQuery.java17
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa2/context/java/GenericJavaNamedQuery2_0.java16
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa2/context/orm/GenericOrmNamedQuery2_0.java23
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/resource/java/binary/BinaryQueryAnnotation.java5
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/resource/java/source/SourceQueryAnnotation.java19
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/jpql/JpaJpqlQueryHelper.java260
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/jpql/XmlEscapeCharacterConverter.java657
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/java/QueryAnnotation.java25
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlNamedNativeQuery.java56
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlNamedQuery.java96
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlQuery.java19
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlContentProposalProvider.java28
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlJavaCompletionProposalComputer.java129
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlSseCompletionProposalComputer.java63
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlCompletionProposal.java84
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlCompletionProposalComputer.java51
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlDefaultCompletionProposal.java65
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlJavaCompletionProposal.java65
-rw-r--r--jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlSseCompletionProposal.java140
23 files changed, 1602 insertions, 275 deletions
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/context/Query.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/context/Query.java
index eab3b0743a..0656d9c838 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/context/Query.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/context/Query.java
@@ -9,6 +9,7 @@
******************************************************************************/
package org.eclipse.jpt.jpa.core.context;
+import java.util.List;
import org.eclipse.jpt.common.core.utility.TextRange;
import org.eclipse.jpt.common.utility.internal.iterable.ArrayIterable;
import org.eclipse.jpt.common.utility.iterable.ListIterable;
@@ -26,7 +27,7 @@ import org.eclipse.jpt.common.utility.iterable.ListIterable;
* stability. It is available at this early stage to solicit feedback from
* pioneering adopters on the understanding that any code that uses this API
* will almost certainly be broken (repeatedly) as the API evolves.
- *
+ *
* @version 3.3
* @since 2.0
*/
@@ -91,11 +92,17 @@ public interface Query
* Move the hint from the source index to the target index.
*/
void moveHint(int targetIndex, int sourceIndex);
-
- QueryHint getHint(int i);
+ QueryHint getHint(int i);
+ /**
+ * Returns the {@link TextRange} of the name property.
+ */
TextRange getNameTextRange();
- TextRange getQueryTextRange();
+ /**
+ * Returns the list of {@link TextRange} of the query property, which is either a single object
+ * if the string is not split or many objects if the JPQL query is split into many strings.
+ */
+ List<TextRange> getQueryTextRanges();
}
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/java/AbstractJavaQuery.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/java/AbstractJavaQuery.java
index 1a40d38f26..913d78f6fd 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/java/AbstractJavaQuery.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/java/AbstractJavaQuery.java
@@ -145,7 +145,7 @@ public abstract class AbstractJavaQuery<A extends QueryAnnotation>
public JavaQueryHint getHint(int index) {
return this.hintContainer.get(index);
}
-
+
protected JavaQueryHint buildHint(QueryHintAnnotation hintAnnotation) {
return this.getJpaFactory().buildJavaQueryHint(this, hintAnnotation);
}
@@ -237,8 +237,8 @@ public abstract class AbstractJavaQuery<A extends QueryAnnotation>
return this.getValidationTextRange(this.queryAnnotation.getNameTextRange());
}
- public TextRange getQueryTextRange() {
- return this.getValidationTextRange(this.queryAnnotation.getQueryTextRange());
+ public List<TextRange> getQueryTextRanges() {
+ return this.queryAnnotation.getQueryTextRanges();
}
public boolean isEquivalentTo(JpaNamedContextNode node) {
@@ -246,7 +246,7 @@ public abstract class AbstractJavaQuery<A extends QueryAnnotation>
(this.getType() == node.getType()) &&
this.isEquivalentTo((Query) node);
}
-
+
protected boolean isEquivalentTo(Query other) {
return ObjectTools.equals(this.name, other.getName()) &&
ObjectTools.equals(this.query, other.getQuery()) &&
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/orm/AbstractOrmQuery.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/orm/AbstractOrmQuery.java
index da6bea6d5a..7ef5d2f624 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/orm/AbstractOrmQuery.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/context/orm/AbstractOrmQuery.java
@@ -10,6 +10,7 @@
package org.eclipse.jpt.jpa.core.internal.context.orm;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import org.eclipse.jpt.common.core.utility.TextRange;
import org.eclipse.jpt.common.utility.internal.ObjectTools;
@@ -31,7 +32,6 @@ import org.eclipse.jpt.jpa.core.jpql.JpaJpqlQueryHelper;
import org.eclipse.jpt.jpa.core.resource.orm.OrmFactory;
import org.eclipse.jpt.jpa.core.resource.orm.XmlQuery;
import org.eclipse.jpt.jpa.core.resource.orm.XmlQueryHint;
-import org.eclipse.persistence.jpa.jpql.ExpressionTools;
import org.eclipse.wst.validation.internal.provisional.core.IMessage;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
@@ -55,7 +55,7 @@ public abstract class AbstractOrmQuery<X extends XmlQuery>
super(parent);
this.xmlQuery = xmlQuery;
this.name = xmlQuery.getName();
- this.query = this.getUnescapedQuery();
+ this.query = this.xmlQuery.getQuery();
this.hintContainer = this.buildHintContainer();
}
@@ -65,7 +65,7 @@ public abstract class AbstractOrmQuery<X extends XmlQuery>
public void synchronizeWithResourceModel() {
super.synchronizeWithResourceModel();
this.setName_(this.xmlQuery.getName());
- this.setQuery_(this.getUnescapedQuery());
+ this.setQuery_(this.xmlQuery.getQuery());
this.syncHints();
}
@@ -111,14 +111,6 @@ public abstract class AbstractOrmQuery<X extends XmlQuery>
this.firePropertyChanged(QUERY_PROPERTY, old, query);
}
- protected String getUnescapedQuery() {
- String queryString = this.xmlQuery.getQuery();
- if (StringTools.isNotBlank(queryString)) {
- queryString = ExpressionTools.unescape(queryString, new int[1]);
- }
- return queryString;
- }
-
// ********** hints **********
@@ -162,7 +154,7 @@ public abstract class AbstractOrmQuery<X extends XmlQuery>
public OrmQueryHint getHint(int index) {
return this.hintContainer.get(index);
}
-
+
protected OrmQueryHint buildHint(XmlQueryHint xmlHint) {
return this.getContextNodeFactory().buildOrmQueryHint(this, xmlHint);
}
@@ -216,7 +208,7 @@ public abstract class AbstractOrmQuery<X extends XmlQuery>
this.addHint().convertFrom(javaQueryHint);
}
}
-
+
// ********** validation **********
public void validate(JpaJpqlQueryHelper queryHelper, List<IMessage> messages, IReporter reporter) {
@@ -266,8 +258,8 @@ public abstract class AbstractOrmQuery<X extends XmlQuery>
return this.getValidationTextRange(this.xmlQuery.getNameTextRange());
}
- public TextRange getQueryTextRange() {
- return this.getValidationTextRange(this.xmlQuery.getQueryTextRange());
+ public List<TextRange> getQueryTextRanges() {
+ return Collections.singletonList(this.xmlQuery.getQueryTextRange());
}
public boolean isEquivalentTo(JpaNamedContextNode node) {
@@ -275,7 +267,7 @@ public abstract class AbstractOrmQuery<X extends XmlQuery>
(this.getType() == node.getType()) &&
this.isEquivalentTo((Query) node);
}
-
+
protected boolean isEquivalentTo(Query other) {
return ObjectTools.equals(this.name, other.getName()) &&
ObjectTools.equals(this.query, other.getQuery()) &&
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa1/context/java/GenericJavaNamedQuery.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa1/context/java/GenericJavaNamedQuery.java
index e9cf3c4ab6..82c27209ca 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa1/context/java/GenericJavaNamedQuery.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa1/context/java/GenericJavaNamedQuery.java
@@ -32,7 +32,7 @@ public class GenericJavaNamedQuery
}
// ********** metadata conversion *********
-
+
public void convertTo(OrmQueryContainer queryContainer) {
queryContainer.addNamedQuery().convertFrom(this);
}
@@ -45,7 +45,15 @@ public class GenericJavaNamedQuery
@Override
protected void validateQuery_(JpaJpqlQueryHelper queryHelper, List<IMessage> messages, IReporter reporter) {
- queryHelper.validate(this, this.query, this.queryAnnotation.getQueryTextRange(), 1, messages);
+ queryHelper.validate(
+ this,
+ this.query,
+ this.query,
+ this.queryAnnotation.getQueryTextRanges(),
+ 1,
+ JpaJpqlQueryHelper.EscapeType.JAVA,
+ messages
+ );
}
// ********** misc **********
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa1/context/orm/GenericOrmNamedQuery.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa1/context/orm/GenericOrmNamedQuery.java
index 6424cdbdae..43c2445d06 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa1/context/orm/GenericOrmNamedQuery.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa1/context/orm/GenericOrmNamedQuery.java
@@ -10,15 +10,14 @@
package org.eclipse.jpt.jpa.core.internal.jpa1.context.orm;
import java.util.List;
-
import org.eclipse.jpt.jpa.core.context.JpaContextNode;
import org.eclipse.jpt.jpa.core.context.NamedQuery;
import org.eclipse.jpt.jpa.core.context.java.JavaNamedQuery;
import org.eclipse.jpt.jpa.core.context.orm.OrmNamedQuery;
import org.eclipse.jpt.jpa.core.internal.context.orm.AbstractOrmQuery;
import org.eclipse.jpt.jpa.core.jpql.JpaJpqlQueryHelper;
+import org.eclipse.jpt.jpa.core.jpql.JpaJpqlQueryHelper.EscapeType;
import org.eclipse.jpt.jpa.core.resource.orm.XmlNamedQuery;
-import org.eclipse.persistence.jpa.jpql.ExpressionTools;
import org.eclipse.wst.validation.internal.provisional.core.IMessage;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
@@ -39,10 +38,18 @@ public class GenericOrmNamedQuery
@Override
protected void validateQuery_(JpaJpqlQueryHelper queryHelper, List<IMessage> messages, IReporter reporter) {
- // Convert the literal escape characters into actual escape characters
- String jpqlQuery = ExpressionTools.unescape(this.query, new int[1]);
+ XmlNamedQuery xmlQuery = this.getXmlQuery();
+ EscapeType escapeType = xmlQuery.isQueryInsideCDATASection() ? EscapeType.NONE : EscapeType.XML;
- queryHelper.validate(this, jpqlQuery, this.getQueryTextRange(), 0, messages);
+ queryHelper.validate(
+ this,
+ this.query,
+ xmlQuery.getActualQuery(),
+ this.getQueryTextRanges(),
+ xmlQuery.getQueryOffset(),
+ escapeType,
+ messages
+ );
}
// ********** misc **********
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa2/context/java/GenericJavaNamedQuery2_0.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa2/context/java/GenericJavaNamedQuery2_0.java
index 177799482d..8dab55a96f 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa2/context/java/GenericJavaNamedQuery2_0.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa2/context/java/GenericJavaNamedQuery2_0.java
@@ -96,7 +96,7 @@ public class GenericJavaNamedQuery2_0
}
// ********** metadata conversion *********
-
+
public void convertTo(OrmQueryContainer queryContainer) {
queryContainer.addNamedQuery().convertFrom(this);
}
@@ -104,12 +104,20 @@ public class GenericJavaNamedQuery2_0
public void delete() {
this.getParent().removeNamedQuery(this);
}
-
+
// ********** validation **********
@Override
protected void validateQuery_(JpaJpqlQueryHelper queryHelper, List<IMessage> messages, IReporter reporter) {
- queryHelper.validate(this, this.query, this.getQueryAnnotation().getQueryTextRange(), 1, messages);
+ queryHelper.validate(
+ this,
+ this.query,
+ this.query,
+ this.queryAnnotation.getQueryTextRanges(),
+ 1,
+ JpaJpqlQueryHelper.EscapeType.JAVA,
+ messages
+ );
}
@Override
@@ -117,7 +125,7 @@ public class GenericJavaNamedQuery2_0
return super.isEquivalentTo(other)
&& this.isEquivalentTo((NamedQuery2_0) other);
}
-
+
protected boolean isEquivalentTo(NamedQuery2_0 other) {
return this.specifiedLockMode == other.getSpecifiedLockMode();
}
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa2/context/orm/GenericOrmNamedQuery2_0.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa2/context/orm/GenericOrmNamedQuery2_0.java
index 0cbc8817c9..26c6efd23b 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa2/context/orm/GenericOrmNamedQuery2_0.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/jpa2/context/orm/GenericOrmNamedQuery2_0.java
@@ -10,7 +10,6 @@
package org.eclipse.jpt.jpa.core.internal.jpa2.context.orm;
import java.util.List;
-
import org.eclipse.jpt.jpa.core.context.JpaContextNode;
import org.eclipse.jpt.jpa.core.context.NamedQuery;
import org.eclipse.jpt.jpa.core.context.Query;
@@ -21,8 +20,8 @@ import org.eclipse.jpt.jpa.core.jpa2.context.NamedQuery2_0;
import org.eclipse.jpt.jpa.core.jpa2.context.java.JavaNamedQuery2_0;
import org.eclipse.jpt.jpa.core.jpa2.context.orm.OrmNamedQuery2_0;
import org.eclipse.jpt.jpa.core.jpql.JpaJpqlQueryHelper;
+import org.eclipse.jpt.jpa.core.jpql.JpaJpqlQueryHelper.EscapeType;
import org.eclipse.jpt.jpa.core.resource.orm.XmlNamedQuery;
-import org.eclipse.persistence.jpa.jpql.ExpressionTools;
import org.eclipse.wst.validation.internal.provisional.core.IMessage;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
@@ -99,7 +98,7 @@ public class GenericOrmNamedQuery2_0
}
// ********** metadata conversion **********
-
+
public void convertFrom(JavaNamedQuery javaQuery) {
super.convertFrom(javaQuery);
this.setSpecifiedLockMode(((JavaNamedQuery2_0)javaQuery).getSpecifiedLockMode());
@@ -110,10 +109,18 @@ public class GenericOrmNamedQuery2_0
@Override
protected void validateQuery_(JpaJpqlQueryHelper queryHelper, List<IMessage> messages, IReporter reporter) {
- // Convert the literal escape characters into actual escape characters
- String jpqlQuery = ExpressionTools.unescape(this.query, new int[1]);
+ XmlNamedQuery xmlQuery = this.getXmlQuery();
+ EscapeType escapeType = xmlQuery.isQueryInsideCDATASection() ? EscapeType.NONE : EscapeType.XML;
- queryHelper.validate(this, jpqlQuery, this.getQueryTextRange(), 0, messages);
+ queryHelper.validate(
+ this,
+ this.query,
+ xmlQuery.getActualQuery(),
+ this.getQueryTextRanges(),
+ xmlQuery.getQueryOffset(),
+ escapeType,
+ messages
+ );
}
@Override
@@ -121,11 +128,11 @@ public class GenericOrmNamedQuery2_0
return super.isEquivalentTo(other)
&& this.isEquivalentTo((NamedQuery) other);
}
-
+
protected boolean isEquivalentTo(NamedQuery namedQuery) {
return this.specifiedLockMode == ((NamedQuery2_0) namedQuery).getSpecifiedLockMode();
}
-
+
// ********** misc **********
public Class<NamedQuery> getType() {
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/resource/java/binary/BinaryQueryAnnotation.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/resource/java/binary/BinaryQueryAnnotation.java
index c90d7c5fc2..ed5195677a 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/resource/java/binary/BinaryQueryAnnotation.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/resource/java/binary/BinaryQueryAnnotation.java
@@ -3,12 +3,13 @@
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
- *
+ *
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.jpa.core.internal.resource.java.binary;
+import java.util.List;
import java.util.Vector;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jpt.common.core.internal.resource.java.binary.BinaryAnnotation;
@@ -101,7 +102,7 @@ abstract class BinaryQueryAnnotation
abstract String getQueryElementName();
- public TextRange getQueryTextRange() {
+ public List<TextRange> getQueryTextRanges() {
throw new UnsupportedOperationException();
}
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/resource/java/source/SourceQueryAnnotation.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/resource/java/source/SourceQueryAnnotation.java
index 8dbb8e0bfd..e1d0bb6000 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/resource/java/source/SourceQueryAnnotation.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/internal/resource/java/source/SourceQueryAnnotation.java
@@ -3,12 +3,13 @@
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
- *
+ *
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.jpa.core.internal.resource.java.source;
+import java.util.List;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jpt.common.core.internal.resource.java.source.SourceAnnotation;
import org.eclipse.jpt.common.core.internal.utility.jdt.ConversionDeclarationAnnotationElementAdapter;
@@ -41,7 +42,7 @@ abstract class SourceQueryAnnotation
DeclarationAnnotationElementAdapter<String> queryDeclarationAdapter;
AnnotationElementAdapter<String> queryAdapter;
String query;
- TextRange queryTextRange;
+ List<TextRange> queryTextRanges;
final QueryHintsAnnotationContainer hintsContainer = new QueryHintsAnnotationContainer();
@@ -60,7 +61,7 @@ abstract class SourceQueryAnnotation
this.name = this.buildName(astAnnotation);
this.nameTextRange = this.buildNameTextRange(astAnnotation);
this.query = this.buildQuery(astAnnotation);
- this.queryTextRange = this.buildQueryTextRange(astAnnotation);
+ this.queryTextRanges = this.buildQueryTextRanges(astAnnotation);
this.hintsContainer.initializeFromContainerAnnotation(astAnnotation);
}
@@ -70,7 +71,7 @@ abstract class SourceQueryAnnotation
this.syncName(this.buildName(astAnnotation));
this.nameTextRange = this.buildNameTextRange(astAnnotation);
this.syncQuery(this.buildQuery(astAnnotation));
- this.queryTextRange = this.buildQueryTextRange(astAnnotation);
+ this.queryTextRanges = this.buildQueryTextRanges(astAnnotation);
this.hintsContainer.synchronize(astAnnotation);
}
@@ -139,12 +140,12 @@ abstract class SourceQueryAnnotation
return this.queryAdapter.getValue(astAnnotation);
}
- public TextRange getQueryTextRange() {
- return this.queryTextRange;
+ public List<TextRange> getQueryTextRanges() {
+ return this.queryTextRanges;
}
- private TextRange buildQueryTextRange(Annotation astAnnotation) {
- return this.getElementTextRange(this.queryDeclarationAdapter, astAnnotation);
+ private List<TextRange> buildQueryTextRanges(Annotation astAnnotation) {
+ return this.getElementTextRanges(this.queryDeclarationAdapter, astAnnotation);
}
private DeclarationAnnotationElementAdapter<String> buildQueryDeclarationAdapter() {
@@ -208,7 +209,7 @@ abstract class SourceQueryAnnotation
/**
* adapt the AnnotationContainer interface to the xml schema's xmlns
*/
- class QueryHintsAnnotationContainer
+ class QueryHintsAnnotationContainer
extends AnnotationContainer<QueryHintAnnotation>
{
@Override
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/jpql/JpaJpqlQueryHelper.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/jpql/JpaJpqlQueryHelper.java
index f07a9fbc63..d2f051fb13 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/jpql/JpaJpqlQueryHelper.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/jpql/JpaJpqlQueryHelper.java
@@ -13,6 +13,7 @@
******************************************************************************/
package org.eclipse.jpt.jpa.core.jpql;
+import java.util.ArrayList;
import java.util.List;
import org.eclipse.jpt.common.core.internal.utility.SimpleTextRange;
import org.eclipse.jpt.common.core.utility.TextRange;
@@ -78,6 +79,44 @@ public abstract class JpaJpqlQueryHelper extends AbstractJPQLQueryHelper {
}
/**
+ * TODO: TO REMOVE ONCE THE NEXT ECLIPSE HERMES MILESTONE IS AVAILABLE.
+ */
+ private static void repositionJava(String query, int[] positions) {
+
+ if ((query == null) || (query.length() == 0)) {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder(query);
+
+ for (int index = 0, count = sb.length(); index < count; index++) {
+
+ char character = sb.charAt(index);
+
+ switch (character) {
+ case '\b': case '\t': case '\n':
+ case '\f': case '\r': case '\"':
+ case '\\': case '\0': case '\1':
+ case '\2': case '\3': case '\4':
+ case '\5': case '\6': case '\7': {
+
+ // Translate both positions because the special
+ // character is written with its escape character
+ if (index < positions[0]) {
+ positions[0]++;
+ positions[1]++;
+ }
+ // Only translate the end position because the start
+ // position is before the current index
+ else if (index < positions[1]) {
+ positions[1]++;
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Create the builder that will create the right implementation of {@link org.eclipse.persistence.
* jpa.jpql.spi.IManagedType IManagedType}.
*
@@ -101,31 +140,53 @@ public abstract class JpaJpqlQueryHelper extends AbstractJPQLQueryHelper {
*
* @param problem The {@link JPQLQueryProblem problem} that was found in the JPQL query, which is
* either a grammatical or semantic problem
- * @param parsedJpqlQuery The string representation of the parsed tree representation of the JPQL
- * query
- * @param actualQuery The actual JPQL query that was parsed and validated
+ * @param parsedJpqlQuery The generated string from {@link org.eclipse.persistence.jpa.jpql.
+ * parser.JPQLExpression JPQLExpression}
+ * @param jpqlQuery The actual JPQL query that was parsed and validated
+ * @param actualJpqlQuery The actual string that is not escaped and found in the document (either
+ * in an XML file or in a Java annotation)
+ * @param offset This offset is used to move the start position
+ * @param escapeType Determines how to escape the JPQL query, if required
* @return The start and end positions, which may have been adjusted
*/
- public int[] buildPositions(JPQLQueryProblem problem, String parsedJpqlQuery, String actualQuery) {
+ public int[] buildPositions(JPQLQueryProblem problem,
+ String parsedJpqlQuery,
+ String jpqlQuery,
+ String actualJpqlQuery,
+ int offset,
+ EscapeType escapeType) {
- int startPosition = problem.getStartPosition();
- int endPosition = problem.getEndPosition();
+ int[] positions = { problem.getStartPosition(), problem.getEndPosition() };
// If the start and end positions are the same, then expand the text range
- if (startPosition == endPosition) {
- startPosition = Math.max(startPosition - 1, 0);
+ if (positions[0] == positions[1]) {
+ positions[0] = Math.max(positions[0] - 1, 0);
}
- // Reposition the cursor so it's correctly positioned in the actual query, which is the
- // since it may contains more than one whitespace for a single whitespace
- int newStartPosition = ExpressionTools.repositionCursor(parsedJpqlQuery, startPosition, actualQuery);
+ // Reposition the cursor so it's correctly positioned in the non-escaped JPQL query
+ // TODO: UPDATE ONCE THE NEXT ECLIPSELINK HERMES 2.5 MILESTONE IS AVAILABLE
+ // ExpressionTools.reposition(parsedJpqlQuery, positions, jpqlQuery);
+ positions[0] = ExpressionTools.repositionCursor(parsedJpqlQuery, positions[0], jpqlQuery);
+ positions[1] = ExpressionTools.repositionCursor(parsedJpqlQuery, positions[1], jpqlQuery);
+
+ // Now add the leading offset
+ positions[0] += offset;
+ positions[1] += offset;
+
+ // Now convert the adjusted positions once again to be in the actual JPQL query that is
+ // found in the document, i.e. that may contain escape characters
+ if (escapeType == EscapeType.JAVA) {
- if (newStartPosition != startPosition) {
- endPosition += (newStartPosition - startPosition);
- startPosition = newStartPosition;
+ // TODO: UPDATE ONCE THE NEXT ECLIPSELINK HERMES 2.5 MILESTONE IS AVAILABLE
+ // ExpressionTools.repositionJava(actualJpqlQuery, positions);
+ repositionJava(actualJpqlQuery, positions);
+ }
+ else if (escapeType == EscapeType.XML) {
+ // TODO: UPDATE ONCE THE NEXT ECLIPSELINK HERMES 2.5 MILESTONE IS AVAILABLE
+ XmlEscapeCharacterConverter.reposition(actualJpqlQuery, positions);
}
- return new int[] { startPosition, endPosition };
+ return positions;
}
/**
@@ -135,51 +196,91 @@ public abstract class JpaJpqlQueryHelper extends AbstractJPQLQueryHelper {
* problem
* @param problem The {@link JPQLQueryProblem problem} that was found in the JPQL query, which is
* either a grammatical or semantic problem
- * @param textRange The range of the JPQL query in the Java source file
- * @param parsedJpqlQuery The string representation of the parsed tree representation of the JPQL
- * query, which may differ from the actual JPQL query since it does not keep more than one
- * whitespace
- * @param actualQuery The actual JPQL query that was parsed and validated
+ * @param textRanges The list of {@link TextRange} objects that represents the JPQL query string
+ * within the document. The list should contain either one {@link TextRange} if the JPQL query is
+ * a single string or many if the JPQL query is split into multiple strings
+ * @param parsedJpqlQuery The generated string from {@link org.eclipse.persistence.jpa.jpql.
+ * parser.JPQLExpression JPQLExpression}
+ * @param jpqlQuery The actual JPQL query that was parsed and validated
+ * @param actualJpqlQuery The actual string that is not escaped and found in the document (either
+ * in an XML file or in a Java annotation)
* @param offset This offset is used to move the start position
- * @return A new {@link IMessage} that has the required information to display the problem
- * underline and the error message in the Problems view
+ * @param escapeType Determines how to escape the JPQL query, if required
+ * @return The list {@link IMessage} objects that has the required information to display the
+ * problem, which support split locations (i.e. for a split strings)
*/
- protected IMessage buildProblem(NamedQuery namedQuery,
- TextRange textRange,
- JPQLQueryProblem problem,
- String parsedJpqlQuery,
- String actualQuery,
- int offset) {
+ protected List<IMessage> buildProblems(NamedQuery namedQuery,
+ List<TextRange> textRanges,
+ JPQLQueryProblem problem,
+ String parsedJpqlQuery,
+ String jpqlQuery,
+ String actualJpqlQuery,
+ int offset,
+ EscapeType escapeType) {
// Convert the positions from the parsed JPQL query to the actual JPQL query
- int[] positions = buildPositions(problem, parsedJpqlQuery, actualQuery);
-
- // Now convert the adjusted positions once again to be in the query where the escape
- // characters are in their literal forms
- int[] newStartPosition = { positions[0] };
- ExpressionTools.escape(actualQuery, newStartPosition);
- int escapeOffset = positions[0] - newStartPosition[0];
-
- positions[0] -= escapeOffset;
- positions[1] -= escapeOffset;
-
- // Create the text range of the problem
- textRange = new SimpleTextRange(
- textRange.getOffset() + positions[0] + offset,
- positions[1] - positions[0],
- textRange.getLineNumber()
- );
+ int[] positions = buildPositions(problem, parsedJpqlQuery, jpqlQuery, actualJpqlQuery, offset, escapeType);
- // Now create the message
- IMessage message = DefaultJpaValidationMessages.buildMessage(
- IMessage.HIGH_SEVERITY,
- problem.getMessageKey(),
- problem.getMessageArguments(),
- namedQuery,
- textRange
- );
- message.setBundleName("jpa_jpql_validation");
- return message;
+ List<IMessage> messages = new ArrayList<IMessage>();
+ int problemLength = positions[1] - positions[0];
+ int problemOffset = positions[0];
+ boolean done = false;
+
+ // Traverse the list of TextRanges in order to properly calculate
+ // the offset within either the single string or the split string
+ for (TextRange textRange : textRanges) {
+
+ // (offset * 2) is to not including the double quotes (specified by the offset) if present
+ int textRangeOffset = (escapeType == EscapeType.JAVA) ? 2 : 0;
+ int textRangeLength = (textRange.getLength() - textRangeOffset);
+
+ // The position of the problem is within the TextRange
+ if (problemOffset <= textRangeLength) {
+
+ // Calculate to see if the problem is entirely within the TextRange,
+ // otherwise it will be divided into two or more TextRanges
+ int partialProblemLength = problemLength;
+
+ if (problemOffset + problemLength > textRangeLength) {
+ partialProblemLength = textRangeLength - problemOffset;
+ problemLength -= partialProblemLength;
+ textRangeLength = 0;
+ }
+ else {
+ done = true;
+ }
+
+ // Now create the TextRange of the problem
+ TextRange problemTextRange = new SimpleTextRange(
+ textRange.getOffset() + problemOffset,
+ partialProblemLength,
+ textRange.getLineNumber()
+ );
+
+ // Create the validation message
+ IMessage message = DefaultJpaValidationMessages.buildMessage(
+ IMessage.HIGH_SEVERITY,
+ problem.getMessageKey(),
+ problem.getMessageArguments(),
+ namedQuery,
+ problemTextRange
+ );
+
+ message.setBundleName("jpa_jpql_validation");
+ messages.add(message);
+
+ // Done traversing the list of TextRanges
+ if (done) {
+ break;
+ }
+ }
+
+ // The problem is not within the TextRange, remove the TextRange length from the
+ // problem's data so it can be properly calculated within a subsequent TextRange
+ problemOffset = (textRangeLength == 0) ? 0 : problemOffset - textRangeLength;
+ }
+
+ return messages;
}
/**
@@ -201,8 +302,9 @@ public abstract class JpaJpqlQueryHelper extends AbstractJPQLQueryHelper {
protected String getValidationPreference(NamedQuery namedQuery) {
return JpaPreferences.getProblemSeverity(
- namedQuery.getResource().getProject(),
- JpaValidationMessages.JPQL_QUERY_VALIDATION);
+ namedQuery.getResource().getProject(),
+ JpaValidationMessages.JPQL_QUERY_VALIDATION
+ );
}
/**
@@ -234,15 +336,20 @@ public abstract class JpaJpqlQueryHelper extends AbstractJPQLQueryHelper {
* @param namedQuery The JPQL query to validate
* @param jpqlQuery The JPQL query, which might be different from what the model object since
* the escape characters should not be in their literal forms (should have '\r' and not '\\r')
- * @param textRange The range of the JPQL query string within the document
+ * @param textRanges The list of {@link TextRange} objects that represents the JPQL query string
+ * within the document. The list should contain either one {@link TextRange} if the JPQL query is
+ * a single string or many if the JPQL query is split into multiple strings
* @param offset This offset is used to move the start position
- * @param messages The list of {@link IMessage IMessages} that will be used to add validation
- * problems
+ * @param messages The list of {@link IMessage IMessages} that will be used to add validation problems
+ * @param escapeCharacters Determines whether the special characters (\n, \r for instance) should
+ * be escaped or not
*/
public void validate(NamedQuery namedQuery,
String jpqlQuery,
- TextRange textRange,
+ String actualJpqlQuery,
+ List<TextRange> textRanges,
int offset,
+ EscapeType escapeType,
List<IMessage> messages) {
try {
@@ -254,16 +361,18 @@ public abstract class JpaJpqlQueryHelper extends AbstractJPQLQueryHelper {
for (JPQLQueryProblem problem : validate()) {
- IMessage message = buildProblem(
+ List<IMessage> results = buildProblems(
namedQuery,
- textRange,
+ textRanges,
problem,
parsedJpqlQuery,
jpqlQuery,
- offset
+ actualJpqlQuery,
+ offset,
+ escapeType
);
- messages.add(message);
+ messages.addAll(results);
}
}
}
@@ -272,4 +381,25 @@ public abstract class JpaJpqlQueryHelper extends AbstractJPQLQueryHelper {
dispose();
}
}
+
+ /**
+ * Constants used to determine how to escape the JPQL query.
+ */
+ public enum EscapeType {
+
+ /**
+ * Escapes some characters when the JPQL query is in an annotation, eg: \t becomes \\t.
+ */
+ JAVA,
+
+ /**
+ * No modification will be performed on the JPQL query.
+ */
+ NONE,
+
+ /**
+ * Escapes XML reserved characters when the JPQL query is in defined xml, eg: > becomes &gt;.
+ */
+ XML
+ }
} \ No newline at end of file
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/jpql/XmlEscapeCharacterConverter.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/jpql/XmlEscapeCharacterConverter.java
new file mode 100644
index 0000000000..321916ba3a
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/jpql/XmlEscapeCharacterConverter.java
@@ -0,0 +1,657 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Oracle. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Oracle - initial API and implementation
+ *
+ ******************************************************************************/
+package org.eclipse.jpt.jpa.core.jpql;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * TODO: TO DELETE ONCE THE NEXT ECLIPSELINK HERMES 2.5 MILESTONE IS AVAILABLE.
+ *
+ * @version 2.5
+ * @since 2.5
+ * @author Pascal Filion
+ */
+@SuppressWarnings("nls")
+public final class XmlEscapeCharacterConverter {
+
+ /**
+ * The entity name for ampersand: <b>&amp;</b>.
+ */
+ public static final String AMPERSAND_ENTITY_NAME = "&amp;";
+
+ /**
+ * The entity name for apostrophe: <b>&apos;</b>.
+ */
+ public static final String APOSTROPHE_ENTITY_NAME = "&apos;";
+
+ /**
+ * The map of symbol mapped to the unicode character.
+ */
+ private static final Map<String, String> dictionary = buildDictionary();
+
+ /**
+ * The entity name for greater-than symbol: <b>&gt;</b>.
+ */
+ public static final String GREATER_THAN_ENTITY_NAME = "&gt;";
+
+ /**
+ * The entity name for less-than symbol: <b>&lt;</b>.
+ */
+ public static final String LESS_THAN_ENTITY_NAME = "&lt;";
+
+ /**
+ * The entity name for quotation mark: <b>&quot;</b>.
+ */
+ public static final String QUOTATION_MARK_NAME = "&quot;";
+
+ /**
+ * Cannot instantiate <code>XmlEscapeCharacterConverter</code>.
+ */
+ private XmlEscapeCharacterConverter() {
+ super();
+ }
+
+ private static Map<String, String> buildDictionary() {
+
+ Map<String, String> dictionary = new HashMap<String, String>();
+
+ // Reserved characters
+ dictionary.put("quot", "\""); // Quotation Mark
+ dictionary.put("apos", "'"); // Apostrophe
+ dictionary.put("amp", "&"); // Ampersand
+ dictionary.put("lt", "<"); // Less Than Symbol
+ dictionary.put("gt", ">"); // Greater Than Symbol
+
+ // ISO-8859-1 symbols
+ dictionary.put("nbsp", "\u00A0"); // Nonbreaking space
+ dictionary.put("iexcl", "\u00A1"); // Inverted Exclamation Point
+ dictionary.put("cent", "\u00A2"); // Cent Sign
+ dictionary.put("pound", "\u00A3"); // Pound Sterling
+ dictionary.put("curren", "\u00A4"); // General Currency Sign
+ dictionary.put("yen", "\u00A5"); // Yen Sign
+ dictionary.put("brvbar", "\u00A6"); // Broken Vertical Bar
+ dictionary.put("sect", "\u00A7"); // Section Sign
+ dictionary.put("uml", "\u00A8"); // Umlaut
+ dictionary.put("copy", "\u00A9"); // Copyright
+ dictionary.put("ordf", "\u00AA"); // Feminine Ordinal
+ dictionary.put("laquo", "\u00AB"); // Left Angle Quote
+ dictionary.put("not", "\u00AC"); // Not Sign
+ dictionary.put("shy", "\u00AD"); // Soft Hyphen
+ dictionary.put("reg", "\u00AE"); // Registered Trademark
+ dictionary.put("macr", "\u00AF"); // Macron Accent
+ dictionary.put("deg", "\u00B0"); // Degree Sign
+ dictionary.put("plusmn", "\u00B1"); // Plus or Minus
+ dictionary.put("sup2", "\u00B2"); // Superscript Two
+ dictionary.put("sup3", "\u00B3"); // Superscript Three
+ dictionary.put("acute", "\u00B4"); // Acute Accent
+ dictionary.put("micro", "\u00B5"); // Micro Sign
+ dictionary.put("para", "\u00B6"); // Paragraph Sign
+ dictionary.put("middot", "\u00B7"); // Middle Dot
+ dictionary.put("cedil", "\u00B8"); // Cedilla
+ dictionary.put("sup1", "\u00B9"); // Superscript One
+ dictionary.put("ordm", "\u00BA"); // Masculine Ordinal
+ dictionary.put("raquo", "\u00BB"); // Right Angle Quote
+ dictionary.put("frac14", "\u00BC"); // Fraction One-Forth
+ dictionary.put("frac12", "\u00BD"); // Fraction One-Half
+ dictionary.put("frac34", "\u00BE"); // Fraction Three-Fourths
+ dictionary.put("iquest", "\u00BF"); // Inverted Question Mark
+ dictionary.put("times", "\u00D7"); // Multiplication
+ dictionary.put("divide", "\u00F7"); // Division
+
+ // ISO-8859-1 characters
+ dictionary.put("Agrave", "\u00C0"); // Latin capital letter A with grave accent
+ dictionary.put("Aacute", "\u00C1"); // Latin capital letter A with acute accent
+ dictionary.put("Acirc", "\u00C2"); // Latin capital letter A with circumflex
+ dictionary.put("Atilde", "\u00C3"); // Latin capital letter A with tilde
+ dictionary.put("Auml", "\u00C4"); // Latin capital letter A with diaeresis
+ dictionary.put("Aring", "\u00C5"); // Latin capital letter A with ring above
+ dictionary.put("AElig", "\u00C6"); // Latin capital letter AE
+ dictionary.put("Ccedil", "\u00C7"); // Latin capital letter C with cedilla
+ dictionary.put("Egrave", "\u00C8"); // Latin capital letter E with grave accent
+ dictionary.put("Eacute", "\u00C9"); // Latin capital letter E with acute accent
+ dictionary.put("Ecirc", "\u00CA"); // Latin capital letter E with circumflex
+ dictionary.put("Euml", "\u00CB"); // Latin capital letter E with diaeresis
+ dictionary.put("Igrave", "\u00CC"); // Latin capital letter I with grave accent
+ dictionary.put("Iacute", "\u00CD"); // Latin capital letter I with acute accent
+ dictionary.put("Icirc", "\u00CE"); // Latin capital letter I with circumflex
+ dictionary.put("Iuml", "\u00CF"); // Latin capital letter I with diaeresis
+ dictionary.put("ETH", "\u00D0"); // Latin capital letter Eth
+ dictionary.put("Ntilde", "\u00D1"); // Latin capital letter N with tilde
+ dictionary.put("Ograve", "\u00D2"); // Latin capital letter O with grave accent
+ dictionary.put("Oacute", "\u00D3"); // Latin capital letter O with acute accent
+ dictionary.put("Ocirc", "\u00D4"); // Latin capital letter O with circumflex
+ dictionary.put("Otilde", "\u00D5"); // Latin capital letter O with tilde
+ dictionary.put("Ouml", "\u00D6"); // Latin capital letter O with diaeresis
+ dictionary.put("Oslash", "\u00D8"); // Latin capital letter O with stroke
+ dictionary.put("Ugrave", "\u00D9"); // Latin capital letter U with grave accent
+ dictionary.put("Uacute", "\u00DA"); // Latin capital letter U with acute accent
+ dictionary.put("Ucirc", "\u00DB"); // Latin capital letter U with circumflex
+ dictionary.put("Uuml", "\u00DC"); // Latin capital letter U with diaeresis
+ dictionary.put("Yacute", "\u00DD"); // Latin capital letter Y with acute accent
+ dictionary.put("THORN", "\u00DE"); // Latin capital letter THORN
+ dictionary.put("szlig", "\u00DF"); // Latin small letter sharp s
+ dictionary.put("agrave", "\u00E0"); // Latin small letter a with grave accent
+ dictionary.put("aacute", "\u00E1"); // Latin small letter a with acute accent
+ dictionary.put("acirc", "\u00E2"); // Latin small letter a with circumflex
+ dictionary.put("atilde", "\u00E3"); // Latin small letter a with tilde
+ dictionary.put("auml", "\u00E4"); // Latin small letter a with diaeresis
+ dictionary.put("aring", "\u00E5"); // Latin small letter a with ring above
+ dictionary.put("aelig", "\u00E6"); // Latin small letter ae
+ dictionary.put("ccedil", "\u00E7"); // Latin small letter c with cedilla
+ dictionary.put("egrave", "\u00E8"); // Latin small letter e with grave accent
+ dictionary.put("eacute", "\u00E9"); // Latin small letter e with acute accent
+ dictionary.put("ecirc", "\u00EA"); // Latin small letter e with circumflex
+ dictionary.put("euml", "\u00EB"); // Latin small letter e with diaeresis
+ dictionary.put("igrave", "\u00EC"); // Latin small letter i with grave accent
+ dictionary.put("iacute", "\u00ED"); // Latin small letter i with acute accent
+ dictionary.put("icirc", "\u00EE"); // Latin small letter i with circumflex
+ dictionary.put("iuml", "\u00EF"); // Latin small letter i with diaeresis
+ dictionary.put("eth", "\u00F0"); // Latin small letter eth
+ dictionary.put("ntilde", "\u00F1"); // Latin small letter n with tilde
+ dictionary.put("ograve", "\u00F2"); // Latin small letter o with grave accent
+ dictionary.put("oacute", "\u00F3"); // Latin small letter o with acute accent
+ dictionary.put("ocirc", "\u00F4"); // Latin small letter o with circumflex
+ dictionary.put("otilde", "\u00F5"); // Latin small letter o with tilde
+ dictionary.put("ouml", "\u00F6"); // Latin small letter o with diaeresis
+ dictionary.put("oslash", "\u00F8"); // Latin small letter o with stroke
+ dictionary.put("ugrave", "\u00F9"); // Latin small letter u with grave accent
+ dictionary.put("uacute", "\u00FA"); // Latin small letter u with acute accent
+ dictionary.put("ucirc", "\u00FB"); // Latin small letter u with circumflex
+ dictionary.put("uuml", "\u00FC"); // Latin small letter u with diaeresis
+ dictionary.put("yacute", "\u00FD"); // Latin small letter y with acute accent
+ dictionary.put("thorn", "\u00FE"); // Latin small letter thorn
+ dictionary.put("yuml", "\u00FF"); // Latin small letter y with diaeresis
+
+ // Math Symbols
+ dictionary.put("forall", "\u2200"); // For all
+ dictionary.put("part", "\u2202"); // Partial differential
+ dictionary.put("exist", "\u2203"); // There exists
+ dictionary.put("empty", "\u2205"); // Empty set; Null Set; Diameter
+ dictionary.put("nabla", "\u2207"); // Nabla; Backward difference
+ dictionary.put("isin", "\u2208"); // Element of
+ dictionary.put("notin", "\u2209"); // Not an element of
+ dictionary.put("ni", "\u220B"); // Contains as member
+ dictionary.put("prod", "\u220F"); // N-ary product; Product sign
+ dictionary.put("sum", "\u2211"); // N-ary sumation
+ dictionary.put("minus", "\u2212"); // Minus sign
+ dictionary.put("lowast", "\u2217"); // Asterisk operator
+ dictionary.put("radic", "\u221A"); // Square root; Radical sign
+ dictionary.put("prop", "\u221D"); // Proportional to
+ dictionary.put("infin", "\u221E"); // Infinity
+ dictionary.put("ang", "\u2220"); // Angle
+ dictionary.put("and", "\u2227"); // Logical and; Wedge
+ dictionary.put("or", "\u2228"); // Logical or; Vee
+ dictionary.put("cap", "\u2229"); // Intersection; Cap
+ dictionary.put("cup", "\u222A"); // Union; Cup
+ dictionary.put("int", "\u222B"); // Integral
+ dictionary.put("there4", "\u2234"); // Therefore
+ dictionary.put("sim", "\u223C"); // Tilde operator; Varies with; Similar to
+ dictionary.put("cong", "\u2245"); // Approximately equal to
+ dictionary.put("asymp", "\u2248"); // Almost equal to; Asymptotic to
+ dictionary.put("ne", "\u2260"); // Not equal to
+ dictionary.put("equiv", "\u2261"); // Identical to
+ dictionary.put("le", "\u2264"); // Less-than or equal to
+ dictionary.put("ge", "\u2265"); // Greater-than or equal to
+ dictionary.put("sub", "\u2282"); // Subset of
+ dictionary.put("sup", "\u2283"); // Superset of
+ dictionary.put("nsub", "\u2284"); // Not a subset of
+ dictionary.put("sube", "\u2286"); // Subset of or equal to
+ dictionary.put("supe", "\u2287"); // Superset of or equal to
+ dictionary.put("oplus", "\u2295"); // Circled plus; Direct sum
+ dictionary.put("otimes", "\u2297"); // Circled times; Vector product
+ dictionary.put("perp", "\u22A5"); // Up tack; Orthogonal to; Perpendicular
+ dictionary.put("sdot", "\u22C5"); // Dot operator
+
+ // Arrows
+ dictionary.put("larr", "\u2190"); // Leftwards arrow
+ dictionary.put("uarr", "\u2191"); // Upwards arrow
+ dictionary.put("rarr", "\u2192"); // Rightwards arrow
+ dictionary.put("darr", "\u2193"); // Downwards arrow
+ dictionary.put("harr", "\u2194"); // Left right arrow
+ dictionary.put("crarr", "\u21B5"); // Downwards arrow with corner leftwards; Carriage return symbol
+ dictionary.put("lArr", "\u21D0"); // Leftwards double arrow
+ dictionary.put("uArr", "\u21D1"); // Upwards double arrow
+ dictionary.put("rArr", "\u21D2"); // Rightwards double arrow
+ dictionary.put("dArr", "\u21D3"); // Downwards double arrow
+ dictionary.put("hArr", "\u21D4"); // Left right double arrow
+
+ // Greek Capital Letters
+ dictionary.put("Alpha", "\u0391"); // Greek capital letter alpha
+ dictionary.put("Beta", "\u0392"); // Greek capital letter beta
+ dictionary.put("Gamma", "\u0393"); // Greek capital letter gamma
+ dictionary.put("Delta", "\u0394"); // Greek capital letter delta
+ dictionary.put("Epsilon", "\u0395"); // Greek capital letter epsilon
+ dictionary.put("Zeta", "\u0396"); // Greek capital letter zeta
+ dictionary.put("Eta", "\u0397"); // Greek capital letter eta
+ dictionary.put("Theta", "\u0398"); // Greek capital letter theta
+ dictionary.put("Iota", "\u0399"); // Greek capital letter iota
+ dictionary.put("Kappa", "\u039A"); // Greek capital letter kappa
+ dictionary.put("Lambda", "\u039B"); // Greek capital letter lambda
+ dictionary.put("Mu", "\u039C"); // Greek capital letter mu
+ dictionary.put("Nu", "\u039D"); // Greek capital letter nu
+ dictionary.put("Xi", "\u039E"); // Greek capital letter xi
+ dictionary.put("Omicron", "\u039F"); // Greek capital letter omicron
+ dictionary.put("Pi", "\u03A0"); // Greek capital letter pi
+ dictionary.put("Rho", "\u03A1"); // Greek capital letter rho
+ dictionary.put("Sigma", "\u03A3"); // Greek capital letter sigma
+ dictionary.put("Tau", "\u03A4"); // Greek capital letter tau
+ dictionary.put("Upsilon", "\u03A5"); // Greek capital letter upsilon
+ dictionary.put("Phi", "\u03A6"); // Greek capital letter phi
+ dictionary.put("Chi", "\u03A7"); // Greek capital letter chi
+ dictionary.put("Psi", "\u03A8"); // Greek capital letter psi
+ dictionary.put("Omega", "\u03A9"); // Greek capital letter omega
+
+ // Greek Small Letters
+ dictionary.put("alpha", "\u03B1"); // Greek small letter alpha
+ dictionary.put("beta", "\u03B2"); // Greek small letter beta
+ dictionary.put("gamma", "\u03B3"); // Greek small letter gamma
+ dictionary.put("delta", "\u03B4"); // Greek small letter delta
+ dictionary.put("epsilon", "\u03B5"); // Greek small letter epsilon
+ dictionary.put("zeta", "\u03B6"); // Greek small letter zeta
+ dictionary.put("eta", "\u03B7"); // Greek small letter eta
+ dictionary.put("theta", "\u03B8"); // Greek small letter theta
+ dictionary.put("iota", "\u03B9"); // Greek small letter iota
+ dictionary.put("kappa", "\u03BA"); // Greek small letter kappa
+ dictionary.put("lambda", "\u03BB"); // Greek small letter lambda
+ dictionary.put("mu", "\u03BC"); // Greek small letter mu
+ dictionary.put("nu", "\u03BD"); // Greek small letter nu
+ dictionary.put("xi", "\u03BE"); // Greek small letter xi
+ dictionary.put("omicron", "\u03BF"); // Greek small letter omicron
+ dictionary.put("pi", "\u03C0"); // Greek small letter pi
+ dictionary.put("rho", "\u03C1"); // Greek small letter rho
+ dictionary.put("sigmaf", "\u03C2"); // Greek small letter final sigma
+ dictionary.put("sigma", "\u03C3"); // Greek small letter sigma
+ dictionary.put("tau", "\u03C4"); // Greek small letter tau
+ dictionary.put("upsilon", "\u03C5"); // Greek small letter upsilon
+ dictionary.put("phi", "\u03C6"); // Greek small letter phi
+ dictionary.put("chi", "\u03C7"); // Greek small letter chi
+ dictionary.put("psi", "\u03C8"); // Greek small letter psi
+ dictionary.put("omega", "\u03C9"); // Greek small letter omega
+ dictionary.put("theta", "\u03D1"); // Greek small letter theta symbol
+ dictionary.put("upsih", "\u03D2"); // Greek upsilon with hook symbol
+ dictionary.put("piv", "\u03D6"); // Greek pi symbol
+
+ // Latin Extended-A and Letterlike Symbols
+ dictionary.put("OElig", "\u0152"); // Latin capital ligature oe
+ dictionary.put("oelig", "\u0153"); // Latin small ligature oe
+ dictionary.put("Scaron", "\u0160"); // Latin capital letter s with caron
+ dictionary.put("scaron", "\u0161"); // Latin small letter s with caron
+ dictionary.put("Yuml", "\u0178"); // Latin capital letter y with diaeresis
+ dictionary.put("fnof", "\u0192"); // Latin small f with hook
+ dictionary.put("weierp", "\u2118"); // Script capital P; Power set; Weierstrass p
+ dictionary.put("image", "\u2111"); // Blackletter capital I; Imaginary part
+ dictionary.put("real", "\u211C"); // Blackletter capital R; Real part symbol
+ dictionary.put("trade", "\u2122"); // Trade mark sign
+ dictionary.put("alefsym", "\u2135"); // Alef symbol; First transfinite cardinal
+
+ // Miscellaneous Shapes
+ dictionary.put("spades", "\u2660"); // Black spade suit
+ dictionary.put("clubs", "\u2663"); // Black club suit; Shamrock
+ dictionary.put("hearts", "\u2665"); // Black heart suit; Valentine
+ dictionary.put("diams", "\u2666"); // Black diamond suit
+ dictionary.put("loz", "\u25CA"); // Lozenge
+
+ // Miscellaneous Technical Symbols
+ dictionary.put("lceil", "\u2308"); // Left ceiling; Apl upstile
+ dictionary.put("rceil", "\u2309"); // Right ceiling
+ dictionary.put("lfloor", "\u230A"); // Left floor; Apl downstile
+ dictionary.put("rfloor", "\u230B"); // Right floor
+ dictionary.put("lang", "\u2329"); // Left-pointing angle bracket
+ dictionary.put("rang", "\u232A"); // Right-pointing angle bracket
+
+ // Spacing Modifier Characters and Bi-directional Characters
+ dictionary.put("circ", "\u02C6"); // Modifier letter circumflex accent
+ dictionary.put("tilde", "\u02DC"); // Small tilde
+ dictionary.put("zwnj", "\u200C"); // Zero width non-joiner
+ dictionary.put("zwj", "\u200D"); // Zero width joiner
+ dictionary.put("lrm", "\u200E"); // Left-to-right mark
+ dictionary.put("rlm", "\u200F"); // Right-to-left mark
+
+ // General Punctuation Set 1
+ dictionary.put("bull", "\u2022"); // Bullet; Black small circle
+ dictionary.put("hellip", "\u2026"); // Horizontal ellipsis; Three dot leader
+ dictionary.put("prime", "\u2032"); // Prime; Minutes; Feet
+ dictionary.put("Prime", "\u2033"); // Double prime; Seconds; Inches
+ dictionary.put("oline", "\u203E"); // Overline; Spacing overscore
+ dictionary.put("frasl", "\u2044"); // Fraction slash
+
+ // General Punctuation Set 2
+ dictionary.put("ensp", "\u2002"); // En space
+ dictionary.put("emsp", "\u2003"); // Em space
+ dictionary.put("thinsp", "\u2009"); // Thin space
+ dictionary.put("zwnj", "\u200C"); // Zero width non-joiner
+ dictionary.put("zwj", "\u200D"); // Zero width joiner
+ dictionary.put("lrm", "\u200E"); // Left-to-right mark
+ dictionary.put("rlm", "\u200F"); // Right-to-left mark
+ dictionary.put("ndash", "\u2013"); // En dash
+ dictionary.put("mdash", "\u2014"); // Em dash
+ dictionary.put("lsquo", "\u2018"); // Left single quotation mark
+ dictionary.put("rsquo", "\u2019"); // Right single quotation mark
+ dictionary.put("sbquo", "\u201A"); // Single low-9 quotation mark
+ dictionary.put("ldquo", "\u201C"); // Left double quotation mark
+ dictionary.put("rdquo", "\u201D"); // Right double quotation mark
+ dictionary.put("bdquo", "\u201E"); // Double low-9 quotation mark
+ dictionary.put("dagger", "\u2020"); // Dagger
+ dictionary.put("Dagger", "\u2021"); // Double dagger
+ dictionary.put("permil", "\u2030"); // Per mille sign
+ dictionary.put("lsaquo", "\u2039"); // Single left-pointing angle quotation mark
+ dictionary.put("rsaquo", "\u203A"); // Single right-pointing angle quotation mark
+ dictionary.put("euro", "\u20AC"); // Euro
+
+ return dictionary;
+ }
+
+ /**
+ * Converts the characters that are reserved in an XML document the given string may have into
+ * their corresponding references (escape characters) using the character entity reference.
+ *
+ * @param value A string that may contain characters that need to be escaped
+ * @param positions This array of length one or two can be used to adjust the position of the
+ * cursor or a text range within the string during the conversion of the reserved characters
+ * @return The given string with any reserved characters converted into the escape characters
+ */
+ public static String escape(String value, int[] positions) {
+
+ if ((value == null) || (value.length() == 0)) {
+ return value;
+ }
+
+ StringBuilder sb = new StringBuilder(value.length());
+ int startPosition = positions[0];
+ int endPosition = (positions.length > 1) ? positions[1] : -1;
+
+ for (int index = 0, count = value.length(); index < count; index++) {
+
+ char character = value.charAt(index);
+
+ // The character is one of the reserved character
+ if (isReserved(character)) {
+
+ // Retrieve the corresponding entity name
+ String name = getEscapeCharacter(character);
+ sb.append(name);
+
+ // Adjust the position
+ if (startPosition > index) {
+ // -1 for the character itself that is replaced by the entity name
+ positions[0] += (name.length() - 1);
+ }
+
+ if ((endPosition > -1) && (index < endPosition)) {
+ // -1 for the character itself that is replaced by the entity name
+ positions[1] += (name.length() - 1);
+ }
+ }
+ else {
+ sb.append(character);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns the Unicode character for the given reference (which is either a numeric character
+ * reference or a character entity reference).
+ *
+ * @param reference The numeric character or character entity reference stripped of the leading
+ * ampersand and trailing semi-colon
+ * @return The Unicode character mapped to the given reference or <code>null</code> if the
+ * reference is invalid or unknown
+ */
+ public static String getCharacter(String reference) {
+
+ if (reference == null) {
+ return null;
+ }
+
+ int length = reference.length();
+
+ if (length == 0) {
+ return null;
+ }
+
+ // Character reference
+ if (reference.charAt(0) == '#') {
+
+ if (length == 1) {
+ return null;
+ }
+
+ // Parse the numeric value
+ String value;
+ int radix;
+
+ // Hexadecimal
+ if (reference.charAt(1) == 'x') {
+ radix = 16;
+ value = reference.substring(2);
+ }
+ // Decimal
+ else {
+ radix = 10;
+ value = reference.substring(1);
+ }
+
+ // No minus accepted
+ if ((value.length() == 0) || (value.charAt(0) == '-')) {
+ return null;
+ }
+
+ // Convert the numeric value into the actual character
+ char character = 0;
+
+ try {
+ character = (char) Integer.parseInt(value, radix);
+ }
+ catch (NumberFormatException ex) {
+ // Simply ignore
+ }
+
+ // The null character &#0; is not permitted
+ if (character == 0) {
+ return null;
+ }
+
+ return String.valueOf(character);
+ }
+
+ // Entity reference
+ return dictionary.get(reference);
+ }
+
+ /**
+ * Returns the escaped character for the given reserved character.
+ *
+ * @param character The reserved character to retrieve its escape character with the entity name
+ * @return The escape character with the entity name of the given character if it is a reserved
+ * character; otherwise returns <code>null</code>
+ */
+ public static String getEscapeCharacter(char character) {
+
+ switch (character) {
+ case '<': return LESS_THAN_ENTITY_NAME;
+ case '>': return GREATER_THAN_ENTITY_NAME;
+ case '&': return AMPERSAND_ENTITY_NAME;
+ case '\'': return APOSTROPHE_ENTITY_NAME;
+ case '\"': return QUOTATION_MARK_NAME;
+ default: return null;
+ }
+ }
+
+ /**
+ * Determines if the given character is one of the XML/HTML reserved characters.
+ *
+ * @param character The character to verify if it's one of the reserved characters
+ * @return <code>true</code> if the given character is defined as a reserved characters;
+ * <code>false</code> otherwise
+ */
+ public static boolean isReserved(char character) {
+
+ switch (character) {
+ case '<':
+ case '>':
+ case '&':
+ case '\'':
+ case '\"': return true;
+ default: return false;
+ }
+ }
+
+ /**
+ * Re-adjusts the given positions, which is based on the non-escaped version of the given
+ * <em>query</em>, by making sure it is pointing at the same position within <em>query</em>,
+ * which contains references (escape characters).
+ * <p>
+ * The escape characters are either the character entity references or the numeric character
+ * references used in an XML document.
+ * <p>
+ * <b>Important:</b> The given query should contain the exact same amount of whitespace than the
+ * query used to calculate the given positions.
+ *
+ * @param query The query that may contain escape characters
+ * @param positions The position within the non-escaped version of the given query, which is
+ * either a single element position or two positions that is used as a text range
+ * @return The adjusted positions by moving it based on the difference between the escape and
+ * non-escaped versions of the query
+ * @since 2.5
+ */
+ public static void reposition(CharSequence query, int[] positions) {
+
+ if ((query == null) || (query.length() == 0)) {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder(query);
+
+ for (int index = 0, count = sb.length(); index < count; index++) {
+
+ char character = sb.charAt(index);
+
+ // The beginning of the escape character
+ if ((character == '&') && (index + 1 < count)) {
+
+ // Find the ending of the escape character
+ int semiColonIndex = sb.indexOf(";", index + 1);
+
+ if (semiColonIndex > -1) {
+
+ // Retrieve the reference value
+ String reference = sb.substring(index + 1, semiColonIndex);
+
+ if (reference.length() > 0) {
+
+ // Retrieve the character mapped to the entity name
+ String unicodeCharacter = XmlEscapeCharacterConverter.getCharacter(reference);
+
+ if (unicodeCharacter != null) {
+
+ // length = '&' + 'reference' + ';' - 'Unicode character'
+ int length = (semiColonIndex - index);
+
+ // Translate both positions because a Unicode
+ // character is written with its escape character
+ if (index < positions[0]) {
+ positions[0] += length;
+ positions[1] += length;
+ }
+ // Only translate the end position because the start
+ // position is before the current index
+ else if (index < positions[1]) {
+ positions[1] += length;
+ }
+
+ index = semiColonIndex;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Converts the references (escape characters) the given string may have into their corresponding
+ * Unicode characters.
+ * <p>
+ * <ul>
+ * <li>Character entity reference: <b>&amp;copy;</b> for <b>&copy;</b></li>
+ * <li>Numeric character reference (decimal value): <b>&amp#169;</b> for <b>&#169;</b></li>
+ * <li>Numeric character reference (hexadecimal value): <b>&amp#xA9;</b> for <b>&#xA9;</b></li>
+ * </ul>
+ * </p>
+ *
+ * @param value A string that may contain escape characters
+ * @param position This array of length one can be used to adjust the position of the cursor
+ * within the string during the conversion of the escape characters
+ * @return The given string with any escape characters converted into the actual Unicode characters
+ */
+ public static String unescape(String value, int[] position) {
+
+ if ((value == null) || (value.length() == 0)) {
+ return value;
+ }
+
+ StringBuilder sb = new StringBuilder(value);
+
+ for (int index = 0, count = sb.length(); index < count; index++) {
+
+ char character = sb.charAt(index);
+
+ // The beginning of the escape character
+ if ((character == '&') && (index + 1 < count)) {
+
+ // Find the ending of the escape character
+ int semiColonIndex = sb.indexOf(";", index + 1);
+
+ if (semiColonIndex > -1) {
+
+ // Retrieve the reference
+ String reference = sb.substring(index + 1, semiColonIndex);
+
+ if (reference.length() > 0) {
+
+ // Retrieve the character mapped to the reference
+ String specialCharacter = getCharacter(reference);
+
+ if (specialCharacter != null) {
+
+ // Replace the reference by the Unicode character
+ sb.replace(index, semiColonIndex + 1, specialCharacter);
+
+ // Make sure the count is updated
+ count -= (semiColonIndex - index);
+
+ // "& + reference + ; - Unicode character"
+ int length = (1 + reference.length());
+
+ // Adjust the position
+ // Case 1: The cursor is within the escape character, move it to the beginning
+ if ((position[0] >= index) && (position[0] <= index + length)) {
+ position[0] = index;
+ }
+ // Case 2: the cursor is after the escape character, just do an adjustment as
+ // if it was a single character
+ else if (position[0] > index + length) {
+ position[0] -= length;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return sb.toString();
+ }
+} \ No newline at end of file
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/java/QueryAnnotation.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/java/QueryAnnotation.java
index 99b72673d9..5a5fb99379 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/java/QueryAnnotation.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/java/QueryAnnotation.java
@@ -3,12 +3,13 @@
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
- *
+ *
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.jpa.core.resource.java;
+import java.util.List;
import org.eclipse.jpt.common.core.resource.java.NestableAnnotation;
import org.eclipse.jpt.common.core.utility.TextRange;
import org.eclipse.jpt.common.utility.iterable.ListIterable;
@@ -23,8 +24,8 @@ import org.eclipse.jpt.common.utility.iterable.ListIterable;
* stability. It is available at this early stage to solicit feedback from
* pioneering adopters on the understanding that any code that uses this API
* will almost certainly be broken (repeatedly) as the API evolves.
- *
- * @version 2.2
+ *
+ * @version 3.3
* @since 2.2
*/
public interface QueryAnnotation
@@ -36,7 +37,7 @@ public interface QueryAnnotation
* Corresponds to the 'name' element of the *Query annotation.
* Return null if the element does not exist in Java.
*/
- String getName();
+ String getName();
String NAME_PROPERTY = "name"; //$NON-NLS-1$
/**
@@ -44,7 +45,7 @@ public interface QueryAnnotation
* Set to null to remove the element.
*/
void setName(String name);
-
+
/**
* Return the {@link TextRange} for the 'name' element. If element
* does not exist return the {@link TextRange} for the *Query annotation.
@@ -58,7 +59,7 @@ public interface QueryAnnotation
* Corresponds to the 'query' element of the *Query annotation.
* Return null if the element does not exist in Java.
*/
- String getQuery();
+ String getQuery();
String QUERY_PROPERTY = "query"; //$NON-NLS-1$
/**
@@ -66,12 +67,12 @@ public interface QueryAnnotation
* Set to null to remove the element.
*/
void setQuery(String query);
-
+
/**
* Return the {@link TextRange} for the 'query' element. If element
* does not exist return the {@link TextRange} for the *Query annotation.
*/
- TextRange getQueryTextRange();
+ List<TextRange> getQueryTextRanges();
// ********** hints **********
@@ -82,7 +83,7 @@ public interface QueryAnnotation
*/
ListIterable<QueryHintAnnotation> getHints();
String HINTS_LIST = "hints"; //$NON-NLS-1$
-
+
/**
* Corresponds to the 'hints' element of the *Query annotation.
*/
@@ -92,12 +93,12 @@ public interface QueryAnnotation
* Corresponds to the 'hints' element of the *Query annotation.
*/
QueryHintAnnotation hintAt(int index);
-
+
/**
* Corresponds to the 'hints' element of the *Query annotation.
*/
QueryHintAnnotation addHint(int index);
-
+
/**
* Corresponds to the 'hints' element of the *Query annotation.
*/
@@ -107,5 +108,5 @@ public interface QueryAnnotation
* Corresponds to the 'hints' element of the *Query annotation.
*/
void removeHint(int index);
-
+
}
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlNamedNativeQuery.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlNamedNativeQuery.java
index dee7d40c87..56ca6faa4a 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlNamedNativeQuery.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlNamedNativeQuery.java
@@ -25,6 +25,7 @@ import org.eclipse.jpt.common.core.utility.TextRange;
import org.eclipse.jpt.jpa.core.resource.orm.v2_0.JPA2_0;
import org.eclipse.jpt.jpa.core.resource.orm.v2_0.OrmV2_0Package;
import org.eclipse.wst.common.internal.emf.resource.Translator;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
/**
* <!-- begin-user-doc -->
@@ -49,6 +50,7 @@ import org.eclipse.wst.common.internal.emf.resource.Translator;
* @model kind="class"
* @generated
*/
+@SuppressWarnings("nls")
public class XmlNamedNativeQuery extends EBaseObjectImpl implements XmlQuery
{
/**
@@ -547,6 +549,60 @@ public class XmlNamedNativeQuery extends EBaseObjectImpl implements XmlQuery
return getAttributeTextRange(JPA.QUERY);
}
+ public int getQueryOffset() {
+
+ IDOMNode node = getElementNode(JPA.QUERY);
+
+ if ((node != null) && node.hasChildNodes()) {
+
+ IDOMNode child = (IDOMNode) node.getFirstChild();
+ String jpqlQuery = child.getTextContent();
+ int offset = 0;
+
+ // Retrieve the length of whitespace before the query, which is the offset to return.
+ // This will help to adjust the cursor position within the query because the actual
+ // string handled by the model does not have those leading whitespace
+ for (int index = 0, count = jpqlQuery.length(); index < count; index++) {
+ if (Character.isWhitespace(jpqlQuery.charAt(index))) {
+ offset++;
+ }
+ else {
+ break;
+ }
+ }
+ return offset;
+ }
+
+ return 0;
+ }
+
+ public String getActualQuery() {
+
+ IDOMNode node = getElementNode(JPA.QUERY);
+
+ if ((node != null) && node.hasChildNodes()) {
+ IDOMNode child = (IDOMNode) node.getFirstChild();
+ if (child.getNodeType() == IDOMNode.CDATA_SECTION_NODE) {
+ return child.getTextContent();
+ }
+ return child.getSource();
+ }
+
+ return null;
+ }
+
+ public boolean isQueryInsideCDATASection() {
+
+ IDOMNode node = getElementNode(JPA.QUERY);
+
+ if ((node != null) && node.hasChildNodes()) {
+ IDOMNode child = (IDOMNode) node.getFirstChild();
+ return (child.getNodeType() == IDOMNode.CDATA_SECTION_NODE);
+ }
+
+ return false;
+ }
+
// ********** translators **********
public static Translator buildTranslator(String elementName, EStructuralFeature structuralFeature) {
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlNamedQuery.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlNamedQuery.java
index 6a84f78953..dc73d1bf00 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlNamedQuery.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlNamedQuery.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2007, 2009 Oracle. All rights reserved.
+ * Copyright (c) 2007, 2012 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
@@ -28,6 +28,7 @@ import org.eclipse.jpt.jpa.core.resource.orm.v2_0.LockModeType_2_0;
import org.eclipse.jpt.jpa.core.resource.orm.v2_0.OrmV2_0Package;
import org.eclipse.jpt.jpa.core.resource.orm.v2_0.XmlNamedQuery_2_0;
import org.eclipse.wst.common.internal.emf.resource.Translator;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
/**
@@ -46,6 +47,7 @@ import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
* @model kind="class"
* @generated
*/
+@SuppressWarnings("nls")
public class XmlNamedQuery extends EBaseObjectImpl implements XmlQuery, XmlNamedQuery_2_0
{
/**
@@ -511,25 +513,103 @@ public class XmlNamedQuery extends EBaseObjectImpl implements XmlQuery, XmlNamed
}
public TextRange getQueryTextRange() {
+
// <named-query>
IDOMNode node = getElementNode(JPA.QUERY);
+
if (node != null) {
+
// The query element has text
if (node.hasChildNodes()) {
- return buildTextRange((IDOMNode) node.getFirstChild());
+ IDOMNode child = (IDOMNode) node.getFirstChild();
+
+ // CDATA section
+ if (child.getNodeType() == IDOMNode.CDATA_SECTION_NODE) {
+ // A CDATA has either 2 sections (the open and close CDATA sections) or 3 sections
+ // (the open CDATA section, the actual content and the close CDATA section)
+ ITextRegionList regions = child.getFirstStructuredDocumentRegion().getRegions();
+ return new SimpleTextRange(
+ child.getStartOffset() + regions.get(1).getStart(),
+ (regions.size() == 3) ? regions.get(1).getLength() : 0,
+ buildTextRange(node).getLineNumber()
+ );
+ }
+ // Generic text node, the following handles the range with escape character,
+ // buildTextRange() uses the length of the converted text
+ else {
+ int startOffset = node.getStartStructuredDocumentRegion().getEndOffset();
+ int endOffset = node.getEndStructuredDocumentRegion().getStartOffset();
+ int lineNumber = child.getStructuredDocument().getLineOfOffset(startOffset) + 1;
+ return new SimpleTextRange(startOffset, endOffset - startOffset, lineNumber);
+ }
}
// The query element does not have text
- TextRange textRange = buildTextRange(node);
- return new SimpleTextRange(
- node.getEndStructuredDocumentRegion().getStartOffset(),
- 0,
- textRange.getLineNumber()
- );
+ else {
+ return new SimpleTextRange(
+ node.getEndStructuredDocumentRegion().getStartOffset(),
+ 0,
+ buildTextRange(node).getLineNumber()
+ );
+ }
}
return this.getValidationTextRange();
}
+ public int getQueryOffset() {
+
+ IDOMNode node = getElementNode(JPA.QUERY);
+
+ if ((node != null) && node.hasChildNodes()) {
+
+ IDOMNode child = (IDOMNode) node.getFirstChild();
+ String jpqlQuery = child.getTextContent();
+ int offset = 0;
+
+ // Retrieve the length of whitespace before the JPQL query, which is the offset to return.
+ // This will help to adjust the cursor position within the JPQL query because the actual
+ // string handled by the model does not have those leading whitespace
+ for (int index = 0, count = jpqlQuery.length(); index < count; index++) {
+ if (Character.isWhitespace(jpqlQuery.charAt(index))) {
+ offset++;
+ }
+ else {
+ break;
+ }
+ }
+ return offset;
+ }
+
+ return 0;
+ }
+
+ public String getActualQuery() {
+
+ IDOMNode node = getElementNode(JPA.QUERY);
+
+ if ((node != null) && node.hasChildNodes()) {
+ IDOMNode child = (IDOMNode) node.getFirstChild();
+ if (child.getNodeType() == IDOMNode.CDATA_SECTION_NODE) {
+ return child.getTextContent();
+ }
+ return child.getSource();
+ }
+
+ return null;
+ }
+
+ public boolean isQueryInsideCDATASection() {
+
+ IDOMNode node = getElementNode(JPA.QUERY);
+
+ if ((node != null) && node.hasChildNodes()) {
+ IDOMNode child = (IDOMNode) node.getFirstChild();
+ return (child.getNodeType() == IDOMNode.CDATA_SECTION_NODE);
+ }
+
+ return false;
+ }
+
// ********** translators **********
public static Translator buildTranslator(String elementName, EStructuralFeature structuralFeature) {
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlQuery.java b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlQuery.java
index df6fded487..aadd05eefb 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlQuery.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core/resource/orm/XmlQuery.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2007, 2009 Oracle. All rights reserved.
+ * Copyright (c) 2007, 2012 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
@@ -66,7 +66,7 @@ public interface XmlQuery extends XmlQuery_2_0
void setName(String value);
/**
- * Returns the value of the '<em><b>Query</b></em>' attribute.
+ * Returns the value of the '<em><b>Query</b></em>' element.
* <!-- begin-user-doc -->
* <p>
* If the meaning of the '<em>Query</em>' attribute isn't clear,
@@ -81,11 +81,24 @@ public interface XmlQuery extends XmlQuery_2_0
*/
String getQuery();
+ String getActualQuery();
+
+ /**
+ * Returns the value of the '<em><b>Query</b></em>' element, including the whitespace before the
+ * JPQL query.
+ */
+ int getQueryOffset();
+
+ /**
+ * Determines whether the JPQL query is inside a CDATA section.
+ */
+ boolean isQueryInsideCDATASection();
+
/**
* Sets the value of the '{@link org.eclipse.jpt.jpa.core.resource.orm.XmlQuery#getQuery <em>Query</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
- * @param value the new value of the '<em>Query</em>' attribute.
+ * @param value the new value of the '<em>Query</em>' element.
* @see #getQuery()
* @generated
*/
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlContentProposalProvider.java b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlContentProposalProvider.java
index bc3a95cdd4..6845c3a351 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlContentProposalProvider.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlContentProposalProvider.java
@@ -66,6 +66,7 @@ import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel;
import org.eclipse.jpt.common.utility.model.value.PropertyValueModel;
import org.eclipse.jpt.jpa.core.context.NamedQuery;
import org.eclipse.jpt.jpa.core.jpql.JpaJpqlQueryHelper;
+import org.eclipse.jpt.jpa.core.jpql.JpaJpqlQueryHelper.EscapeType;
import org.eclipse.jpt.jpa.ui.internal.JptUiMessages;
import org.eclipse.osgi.util.NLS;
import org.eclipse.persistence.jpa.jpql.JPQLQueryProblem;
@@ -347,19 +348,18 @@ public final class JpaJpqlContentProposalProvider extends JpqlCompletionProposal
Image image,
int cursorOffset) {
- return new JpqlCompletionProposal(
+ return new JpqlDefaultCompletionProposal(
contentAssistProposals,
proposal,
displayString,
additionalInfo,
image,
namedQuery,
- actualQuery,
jpqlQuery,
- offset,
+ tokenStart,
+ tokenEnd,
position,
- cursorOffset,
- false
+ cursorOffset
);
}
@@ -472,6 +472,9 @@ public final class JpaJpqlContentProposalProvider extends JpqlCompletionProposal
// Install a custom context menu to the widget
TextTransferHandler.installContextMenu(styledText, sourceViewer.getUndoManager());
+
+ // Make sure the document is up to date in case the text holder is already hooked to the model
+ sourceViewer.getDocument().set(textHolder.getValue());
}
/**
@@ -583,8 +586,8 @@ public final class JpaJpqlContentProposalProvider extends JpqlCompletionProposal
for (JPQLQueryProblem problem : sortProblems(queryHelper.validate())) {
- // Create the range
- int[] positions = queryHelper.buildPositions(problem, parsedJpqlQuery, jpqlQuery);
+ // Create the text range
+ int[] positions = queryHelper.buildPositions(problem, parsedJpqlQuery, jpqlQuery, jpqlQuery, 0, EscapeType.NONE);
// Add the problem to the tool tip
Annotation annotation = new Annotation(ERROR_TYPE, true, buildMessage(problem));
@@ -608,9 +611,16 @@ public final class JpaJpqlContentProposalProvider extends JpqlCompletionProposal
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
JpaJpqlContentProposalProvider.this.position = offset;
-
String jpqlQuery = viewer.getDocument().get();
- List<ICompletionProposal> proposals = buildProposals(query(), jpqlQuery, 0, position);
+
+ List<ICompletionProposal> proposals = buildProposals(
+ query(),
+ jpqlQuery,
+ 0,
+ jpqlQuery.length(),
+ position
+ );
+
return proposals.toArray(new ICompletionProposal[proposals.size()]);
}
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlJavaCompletionProposalComputer.java b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlJavaCompletionProposalComputer.java
index 9aed1daa34..50f095a33d 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlJavaCompletionProposalComputer.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlJavaCompletionProposalComputer.java
@@ -13,6 +13,7 @@
******************************************************************************/
package org.eclipse.jpt.jpa.ui.internal.jpql;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.resources.IFile;
@@ -27,6 +28,7 @@ import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.IExtendedModifier;
+import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
@@ -55,7 +57,7 @@ import org.eclipse.swt.graphics.Image;
* This computer adds content assist support when it is invoked inside the query element of {@link
* javax.persistence.NamedQuery &#64;NamedQuery}.
*
- * @version 3.2
+ * @version 3.3
* @since 3.0
* @author Pascal Filion
*/
@@ -80,22 +82,30 @@ public final class JpaJpqlJavaCompletionProposalComputer extends JpqlCompletionP
Image image,
int cursorOffset) {
- return new JpqlCompletionProposal(
+ return new JpqlJavaCompletionProposal(
contentAssistProposals,
proposal,
displayString,
additionalInfo,
image,
namedQuery,
- actualQuery,
jpqlQuery,
- offset + 1, // +1 is to skip the opening "
+ tokenStart + 1, // +1 is to skip the opening "
+ tokenEnd - 1, // -1 is to skip the closing "
position,
- cursorOffset,
- true
+ cursorOffset
);
}
+ @SuppressWarnings("unchecked")
+ private List<Expression> children(InfixExpression expression) {
+ List<Expression> children = new ArrayList<Expression>();
+ children.add(expression.getLeftOperand());
+ children.add(expression.getRightOperand());
+ children.addAll(expression.extendedOperands());
+ return children;
+ }
+
/**
* {@inheritDoc}
*/
@@ -129,10 +139,11 @@ public final class JpaJpqlJavaCompletionProposalComputer extends JpqlCompletionP
// - It is inside the string representation of a unicode character, \\u0|0E9 where | is the
// cursor, then -1 is returned;
// - The string is not valid (it has some invalid characters)
- int tokenStart = completionContext.getTokenStart();
- if (tokenStart == -1) return Collections.emptyList();
+ int tokenStart[] = { completionContext.getTokenStart() };
+ int tokenEnd[] = { completionContext.getTokenEnd() };
+ if (tokenStart[0] == -1) return Collections.emptyList();
- int[] position = { completionContext.getOffset() - tokenStart - 1 };
+ int[] position = { completionContext.getOffset() - tokenStart[0] - 1 };
if (position[0] < 0) return Collections.emptyList();
ICompilationUnit compilationUnit = context.getCompilationUnit();
@@ -149,15 +160,15 @@ public final class JpaJpqlJavaCompletionProposalComputer extends JpqlCompletionP
checkCanceled(monitor);
// Retrieve the JPA's model object
- NamedQuery namedQuery = namedQuery(jpaFile, tokenStart);
+ NamedQuery namedQuery = namedQuery(jpaFile, tokenStart[0]);
if (namedQuery == null) return Collections.emptyList();
// Retrieve the actual value of the element "query" since the content assist can be
// invoked before the model received the new content
- String jpqlQuery = jpqlQuery(astRoot, tokenStart, completionContext.getTokenEnd(), position);
+ String jpqlQuery = retrieveQuery(astRoot, tokenStart, tokenEnd, position);
// Now create the proposals
- return buildProposals(namedQuery, jpqlQuery, tokenStart, position[0]);
+ return buildProposals(namedQuery, jpqlQuery, tokenStart[0], tokenEnd[0], position[0]);
}
/**
@@ -178,10 +189,11 @@ public final class JpaJpqlJavaCompletionProposalComputer extends JpqlCompletionP
for (Query query : typeMapping.getQueries()){
if (query.getType().equals(NamedQuery.class)){
- JavaNamedQuery namedQuery = (JavaNamedQuery)query;
- TextRange textRange = namedQuery.getQueryAnnotation().getQueryTextRange();
- if ((textRange != null) && textRange.includes(tokenStart)) {
- return namedQuery;
+ JavaNamedQuery namedQuery = (JavaNamedQuery) query;
+ for (TextRange textRange : namedQuery.getQueryAnnotation().getQueryTextRanges()) {
+ if ((textRange != null) && textRange.includes(tokenStart)) {
+ return namedQuery;
+ }
}
}
}
@@ -206,20 +218,6 @@ public final class JpaJpqlJavaCompletionProposalComputer extends JpqlCompletionP
startPosition + node.getLength() >= tokenEnd;
}
- private String jpqlQuery(CompilationUnit astRoot, int tokenStart, int tokenEnd, int[] position) {
-
- String jpqlQuery = retrieveQuery(astRoot, tokenStart, tokenEnd);
-
- if (jpqlQuery == null) {
- jpqlQuery = StringTools.EMPTY_STRING;
- }
- else if (StringTools.isQuoted(jpqlQuery)) {
- jpqlQuery = jpqlQuery.substring(1, jpqlQuery.length() - 1);
- }
-
- return jpqlQuery;
- }
-
/**
* {@inheritDoc}
*/
@@ -254,12 +252,12 @@ public final class JpaJpqlJavaCompletionProposalComputer extends JpqlCompletionP
* @return The actual value retrieved from the query element
*/
@SuppressWarnings("unchecked")
- private String retrieveQuery(CompilationUnit astRoot, int tokenStart, int tokenEnd) {
+ private String retrieveQuery(CompilationUnit astRoot, int[] tokenStart, int[] tokenEnd, int[] position) {
// Dig into the TypeDeclarations
for (AbstractTypeDeclaration type : (List<AbstractTypeDeclaration>) astRoot.types()) {
- if (isInsideNode(type, tokenStart, tokenEnd)) {
+ if (isInsideNode(type, tokenStart[0], tokenEnd[0])) {
// Dig inside its modifiers and annotations
for (IExtendedModifier modifier : (List<IExtendedModifier>) type.modifiers()) {
@@ -271,7 +269,7 @@ public final class JpaJpqlJavaCompletionProposalComputer extends JpqlCompletionP
Annotation annotation = (Annotation) modifier;
// Dig inside the annotation
- if (isInsideNode(annotation, tokenStart, tokenEnd)) {
+ if (isInsideNode(annotation, tokenStart[0], tokenEnd[0])) {
// @NamedQueries({...})
if (annotation.isSingleMemberAnnotation()) {
@@ -282,22 +280,22 @@ public final class JpaJpqlJavaCompletionProposalComputer extends JpqlCompletionP
ArrayInitializer array = (ArrayInitializer) value;
for (Expression expression : (List<Expression>) array.expressions()) {
- if (isInsideNode(expression, tokenStart, tokenEnd)) {
- return retrieveQuery((NormalAnnotation) expression, tokenStart, tokenEnd);
+ if (isInsideNode(expression, tokenStart[0], tokenEnd[0])) {
+ return retrieveQuery((NormalAnnotation) expression, tokenStart, tokenEnd, position);
}
}
}
else {
NormalAnnotation childAnnotation = (NormalAnnotation) value;
- if (isInsideNode(childAnnotation, tokenStart, tokenEnd)) {
- return retrieveQuery(childAnnotation, tokenStart, tokenEnd);
+ if (isInsideNode(childAnnotation, tokenStart[0], tokenEnd[0])) {
+ return retrieveQuery(childAnnotation, tokenStart, tokenEnd, position);
}
}
}
// @NamedQuery()
else if (annotation.isNormalAnnotation()) {
- return retrieveQuery((NormalAnnotation) annotation, tokenStart, tokenEnd);
+ return retrieveQuery((NormalAnnotation) annotation, tokenStart, tokenEnd, position);
}
}
}
@@ -308,17 +306,62 @@ public final class JpaJpqlJavaCompletionProposalComputer extends JpqlCompletionP
}
@SuppressWarnings("unchecked")
- private String retrieveQuery(NormalAnnotation annotation, int tokenStart, int tokenEnd) {
+ private String retrieveQuery(NormalAnnotation annotation, int[] tokenStart, int[] tokenEnd, int[] position) {
for (MemberValuePair pair : (List<MemberValuePair>) annotation.values()) {
org.eclipse.jdt.core.dom.Expression expression = pair.getValue();
- if (isInsideNode(expression, tokenStart, tokenEnd)) {
- StringLiteral literal = (StringLiteral) pair.getValue();
- return literal.getEscapedValue();
+ if (isInsideNode(expression, tokenStart[0], tokenEnd[0])) {
+ Expression child = pair.getValue();
+
+ // Single string
+ if (child.getNodeType() == ASTNode.STRING_LITERAL) {
+ StringLiteral literal = (StringLiteral) pair.getValue();
+ return unquotedString(literal.getEscapedValue());
+ }
+
+ // Build the JPQL query from the concatenated strings
+ if (child.getNodeType() == ASTNode.INFIX_EXPRESSION) {
+
+ StringBuilder sb = new StringBuilder();
+ boolean adjustPosition = true;
+
+ for (Expression childNode : children((InfixExpression) child)) {
+
+ StringLiteral literal = (StringLiteral) childNode;
+ sb.append(unquotedString(literal.getEscapedValue()));
+
+ if (adjustPosition && !isInsideNode(literal, tokenEnd[0], tokenEnd[0])) {
+ position[0] += (literal.getLength() - 2);
+ }
+ else {
+ adjustPosition = false;
+ }
+ }
+
+ // Now adjust the start and end offsets so it includes the entire InfixExpression
+ // because content assist will only replace one string literal and right now we
+ // only support replacing the entire string
+ tokenStart[0] = child.getStartPosition();
+ tokenEnd[0] = child.getStartPosition() + child.getLength();
+
+ return sb.toString();
+ }
}
}
- return null;
+ return StringTools.EMPTY_STRING;
+ }
+
+ private String unquotedString(String value) {
+
+ if (value == null) {
+ value = StringTools.EMPTY_STRING;
+ }
+ else if (StringTools.isQuoted(value)) {
+ value = value.substring(1, value.length() - 1);
+ }
+
+ return value;
}
} \ No newline at end of file
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlSseCompletionProposalComputer.java b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlSseCompletionProposalComputer.java
index 92b4bb3fe9..e3299b054a 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlSseCompletionProposalComputer.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpaJpqlSseCompletionProposalComputer.java
@@ -36,9 +36,9 @@ import org.eclipse.jpt.jpa.core.context.orm.EntityMappings;
import org.eclipse.jpt.jpa.core.context.orm.OrmEntity;
import org.eclipse.jpt.jpa.core.context.orm.OrmNamedQuery;
import org.eclipse.jpt.jpa.core.context.orm.OrmQueryContainer;
+import org.eclipse.jpt.jpa.core.jpql.XmlEscapeCharacterConverter;
import org.eclipse.jpt.jpa.ui.internal.JptUiMessages;
import org.eclipse.jpt.jpa.ui.internal.plugin.JptJpaUiPlugin;
-import org.eclipse.persistence.jpa.jpql.ExpressionTools;
import org.eclipse.swt.graphics.Image;
import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext;
import org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer;
@@ -47,13 +47,15 @@ import org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer;
* This computer adds content assist support when it is invoked inside the &lt;query&gt; element
* defined in a mapping file (ORM Configuration).
*
- * @version 3.2
+ * @version 3.3
* @since 3.0
* @author Pascal Filion
*/
public final class JpaJpqlSseCompletionProposalComputer extends JpqlCompletionProposalComputer<ICompletionProposal>
implements ICompletionProposalComputer {
+ private boolean cDATASection;
+
/**
* Creates a new <code>JpaJpqlSseCompletionProposalComputer</code>.
*/
@@ -71,19 +73,21 @@ public final class JpaJpqlSseCompletionProposalComputer extends JpqlCompletionPr
Image image,
int cursorOffset) {
- return new JpqlCompletionProposal(
+ return new JpqlSseCompletionProposal(
contentAssistProposals,
proposal,
displayString,
additionalInfo,
image,
namedQuery,
- actualQuery,
jpqlQuery,
- offset,
+ actualQuery,
+ tokenStart,
+ tokenEnd,
position,
+ actualPosition,
cursorOffset,
- false
+ cDATASection
);
}
@@ -117,12 +121,17 @@ public final class JpaJpqlSseCompletionProposalComputer extends JpqlCompletionPr
OrmNamedQuery namedQuery = namedQuery(jpaFile, offset, position);
if (namedQuery == null) return Collections.emptyList();
- // Keep track of the beginning of the text since the entire string is always replaced
+ // Retrieve the entire JPQL query from the document
+ Object[] info = jpqlQuery(namedQuery, context.getDocument());
+ String jpqlQuery = (String) info[0];
+ this.cDATASection = (Boolean) info[1];
+
+ // Keep track of the beginning and ending offsets since the entire string is always replaced
int tokenStart = offset - position[0];
+ int tokenEnd = tokenStart + jpqlQuery.length();
// Now create the proposals
- String jpqlQuery = jpqlQuery(namedQuery, context.getDocument());
- return buildProposals(namedQuery, jpqlQuery, tokenStart, position[0]);
+ return buildProposals(namedQuery, jpqlQuery, tokenStart, tokenEnd, position[0]);
}
catch (Exception ex) {
JptJpaUiPlugin.instance().logError(ex, JptUiMessages.JpaJpqlSseCompletionProposalComputer_Error);
@@ -175,24 +184,34 @@ public final class JpaJpqlSseCompletionProposalComputer extends JpqlCompletionPr
private OrmNamedQuery findNamedQuery(OrmQueryContainer container, int offset, int[] position) {
for (OrmNamedQuery namedQuery : container.getNamedQueries()) {
- TextRange textRange = namedQuery.getQueryTextRange();
- if (textRange.touches(offset)) {
- position[0] = offset - textRange.getOffset();
- return namedQuery;
+ for (TextRange textRange : namedQuery.getQueryTextRanges()) {
+
+ if (textRange.touches(offset)) {
+ position[0] = offset - textRange.getOffset();
+ return namedQuery;
+ }
}
}
return null;
}
- private String jpqlQuery(OrmNamedQuery namedQuery, IDocument document) {
+ private Object[] jpqlQuery(OrmNamedQuery namedQuery, IDocument document) {
+
try {
- TextRange range = namedQuery.getQueryTextRange();
- return document.get(range.getOffset(), range.getLength());
+ TextRange range = namedQuery.getQueryTextRanges().get(0);
+
+ return new Object[] {
+ document.get(range.getOffset(), range.getLength()),
+ namedQuery.getXmlQuery().isQueryInsideCDATASection()
+ };
}
catch (BadLocationException e) {
- return StringTools.EMPTY_STRING;
+ return new Object[] {
+ StringTools.EMPTY_STRING,
+ Boolean.FALSE
+ };
}
}
@@ -201,7 +220,15 @@ public final class JpaJpqlSseCompletionProposalComputer extends JpqlCompletionPr
*/
@Override
String modifyJpqlQuery(String jpqlQuery, int[] position) {
- return ExpressionTools.unescape(jpqlQuery, position);
+
+ // The JPQL query is not encapsulated by a CDATA section,
+ // converts any escape characters like &lt; into '<'
+ if (!cDATASection) {
+ // TODO: UPDATE TO USE ECLIPSELINK HERMES 2.5 M2 ONCE IT IS AVAILABLE
+ jpqlQuery = XmlEscapeCharacterConverter.unescape(jpqlQuery, position);
+ }
+
+ return jpqlQuery;
}
private OrmNamedQuery namedQuery(JpaFile jpaFile, int offset, int[] position) {
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlCompletionProposal.java b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlCompletionProposal.java
index aefd14b501..cf9b6f53f1 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlCompletionProposal.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlCompletionProposal.java
@@ -26,29 +26,28 @@ import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
/**
- * The concrete implementation of a {@link org.eclipse.jface.text.contentassist.ICompletionProposal
- * ICompletionProposal} that adds relevance and toggling the completion insertion property behavior.
+ * The abstract implementation of {@link org.eclipse.jface.text.contentassist.ICompletionProposal
+ * ICompletionProposal} which adds relevance and toggling the completion insertion property behavior.
*
- * @version 3.0
+ * @version 3.3
* @since 3.0
* @author Pascal Filion
*/
-final class JpqlCompletionProposal implements ICompletionProposal {
+abstract class JpqlCompletionProposal implements ICompletionProposal {
- private String actualQuery;
private String additionalInfo;
- private ContentAssistProposals proposals;
private int cursorOffset;
private String displayString;
- private boolean escapeCharacters;
private Image image;
- private String jpqlQuery;
+ String jpqlQuery;
private NamedQuery namedQuery;
- private int offset;
- private int position;
- private String proposal;
- private ResultQuery result;
- private boolean toggleCompletion;
+ int position;
+ String proposal;
+ ContentAssistProposals proposals;
+ ResultQuery result;
+ boolean toggleCompletion;
+ private int tokenEnd;
+ private int tokenStart;
JpqlCompletionProposal(ContentAssistProposals proposals,
String proposal,
@@ -56,27 +55,25 @@ final class JpqlCompletionProposal implements ICompletionProposal {
String additionalInfo,
Image image,
NamedQuery namedQuery,
- String actualQuery,
String jpqlQuery,
- int offset,
+ int tokenStart,
+ int tokenEnd,
int position,
- int cursorOffset,
- boolean escapeCharacters) {
+ int cursorOffset) {
super();
this.image = image;
- this.offset = offset;
+ this.tokenStart = tokenStart;
+ this.tokenEnd = tokenEnd;
this.position = position;
this.proposal = proposal;
this.jpqlQuery = jpqlQuery;
this.proposals = proposals;
this.namedQuery = namedQuery;
- this.actualQuery = actualQuery;
this.cursorOffset = cursorOffset;
this.displayString = displayString;
this.additionalInfo = additionalInfo;
- this.escapeCharacters = escapeCharacters;
}
/**
@@ -84,35 +81,19 @@ final class JpqlCompletionProposal implements ICompletionProposal {
*/
public void apply(IDocument document) {
try {
- ResultQuery result = buildResult();
- document.replace(offset, actualQuery.length(), result.getQuery());
+ document.replace(tokenStart, tokenEnd - tokenStart, getResult().getQuery());
}
catch (BadLocationException e) {
// Ignore
}
}
- private ResultQuery buildResult() {
- if (result == null) {
- if (escapeCharacters) {
- result = proposals.buildEscapedQuery(
- jpqlQuery,
- proposal,
- position,
- isCompletionInserts() ^ toggleCompletion
- );
- }
- else {
- result = proposals.buildQuery(
- jpqlQuery,
- proposal,
- position,
- isCompletionInserts() ^ toggleCompletion
- );
- }
- }
- return result;
- }
+ /**
+ * Creates
+ *
+ * @return
+ */
+ abstract ResultQuery buildResult();
/**
* {@inheritDoc}
@@ -143,14 +124,25 @@ final class JpqlCompletionProposal implements ICompletionProposal {
}
/**
+ * Returns
+ *
+ * @return
+ */
+ final ResultQuery getResult() {
+ if (result == null) {
+ result = buildResult();
+ }
+ return result;
+ }
+
+ /**
* {@inheritDoc}
*/
public Point getSelection(IDocument document) {
- ResultQuery result = buildResult();
- return new Point(offset + result.getPosition() + cursorOffset, 0);
+ return new Point(tokenStart + getResult().getPosition() + cursorOffset, 0);
}
- private boolean isCompletionInserts() {
+ final boolean isCompletionInserts() {
IJavaProject javaProject = namedQuery.getJpaProject().getJavaProject();
String value = PreferenceConstants.getPreference(PreferenceConstants.CODEASSIST_INSERT_COMPLETION, javaProject);
return Boolean.valueOf(value);
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlCompletionProposalComputer.java b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlCompletionProposalComputer.java
index 7517ff0c22..530173c2f9 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlCompletionProposalComputer.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlCompletionProposalComputer.java
@@ -47,6 +47,11 @@ import static org.eclipse.persistence.jpa.jpql.spi.IMappingType.*;
abstract class JpqlCompletionProposalComputer<T> {
/**
+ * The position of the cursor within the actual JPQL query (not modified).
+ */
+ int actualPosition;
+
+ /**
* The current value of the query element.
*/
String actualQuery;
@@ -73,17 +78,12 @@ abstract class JpqlCompletionProposalComputer<T> {
NamedQuery namedQuery;
/**
- * The start position of the query within the document.
- */
- int offset;
-
- /**
* The word before the position of the cursor.
*/
private String partialWord;
/**
- * The position of the cursor within {@link #actualQuery}.
+ * The position of the cursor within the adjusted JPQL query.
*/
int position;
@@ -94,6 +94,16 @@ abstract class JpqlCompletionProposalComputer<T> {
JpaJpqlQueryHelper queryHelper;
/**
+ * The end position of the query within the document.
+ */
+ int tokenEnd;
+
+ /**
+ * The start position of the query within the document.
+ */
+ int tokenStart;
+
+ /**
* Creates a new <code>JpqlCompletionProposalComputer</code>.
*/
public JpqlCompletionProposalComputer() {
@@ -219,6 +229,10 @@ abstract class JpqlCompletionProposalComputer<T> {
);
}
+ private ImageDescriptor buildImageDescriptor(String key) {
+ return JptJpaUiPlugin.instance().buildImageDescriptor(key);
+ }
+
private T buildMappingProposal(IMapping mapping) {
String proposal = mapping.getName();
return buildProposal(proposal, proposal, mappingImage(mapping));
@@ -252,19 +266,25 @@ abstract class JpqlCompletionProposalComputer<T> {
* the specified position.
*
* @param namedQuery The model object used to access the application metadata information
- * @param actualQuery The model object may sometimes be out of sync with the actual content, the
- * actual query is required for proper content assist
+ * @param actualQuery The string representation of the JPQL query that is coming from the
+ * document itself (Java source or XML)
* @param offset The beginning of the string within the document
* @param position The position of the cursor within the query, which starts at the beginning of
* that query and not the document
* @return The list of completion proposals
*/
- final List<T> buildProposals(NamedQuery namedQuery, String actualQuery, int offset, int position) {
+ final List<T> buildProposals(NamedQuery namedQuery,
+ String actualQuery,
+ int tokenStart,
+ int tokenEnd,
+ int position) {
try {
- this.offset = offset;
- this.actualQuery = actualQuery;
- this.namedQuery = namedQuery;
+ this.tokenStart = tokenStart;
+ this.tokenEnd = tokenEnd;
+ this.actualQuery = actualQuery;
+ this.actualPosition = position;
+ this.namedQuery = namedQuery;
// It's possible the string has literal representation of the escape characters, if required,
// convert them into actual escape characters and adjust the position accordingly
@@ -309,7 +329,8 @@ abstract class JpqlCompletionProposalComputer<T> {
*/
final void clearInformation() {
namedQuery = null;
- offset = -1;
+ tokenStart = -1;
+ tokenEnd = -1;
position = -1;
actualQuery = null;
namedQuery = null;
@@ -341,10 +362,6 @@ abstract class JpqlCompletionProposalComputer<T> {
return image;
}
- private ImageDescriptor buildImageDescriptor(String key) {
- return JptJpaUiPlugin.instance().buildImageDescriptor(key);
- }
-
private synchronized ImageRegistry getImageRegistry() {
if (imageRegistry == null) {
imageRegistry = new ImageRegistry(Display.getCurrent());
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlDefaultCompletionProposal.java b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlDefaultCompletionProposal.java
new file mode 100644
index 0000000000..8e3ee82187
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlDefaultCompletionProposal.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Oracle. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Oracle - initial API and implementation
+ *
+ ******************************************************************************/
+package org.eclipse.jpt.jpa.ui.internal.jpql;
+
+import org.eclipse.jpt.jpa.core.context.NamedQuery;
+import org.eclipse.persistence.jpa.jpql.ContentAssistProposals;
+import org.eclipse.persistence.jpa.jpql.ResultQuery;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * @version 3.3
+ * @since 3.3
+ * @author Pascal Filion
+ */
+final class JpqlDefaultCompletionProposal extends JpqlCompletionProposal {
+
+ JpqlDefaultCompletionProposal(ContentAssistProposals proposals,
+ String proposal,
+ String displayString,
+ String additionalInfo,
+ Image image,
+ NamedQuery namedQuery,
+ String jpqlQuery,
+ int tokenStart,
+ int tokenEnd,
+ int position,
+ int cursorOffset) {
+
+ super(proposals,
+ proposal,
+ displayString,
+ additionalInfo,
+ image,
+ namedQuery,
+ jpqlQuery,
+ tokenStart,
+ tokenEnd,
+ position,
+ cursorOffset);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ ResultQuery buildResult() {
+ return proposals.buildQuery(
+ jpqlQuery,
+ proposal,
+ position,
+ isCompletionInserts() ^ toggleCompletion
+ );
+ }
+} \ No newline at end of file
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlJavaCompletionProposal.java b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlJavaCompletionProposal.java
new file mode 100644
index 0000000000..a787b7a0a8
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlJavaCompletionProposal.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Oracle. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Oracle - initial API and implementation
+ *
+ ******************************************************************************/
+package org.eclipse.jpt.jpa.ui.internal.jpql;
+
+import org.eclipse.jpt.jpa.core.context.NamedQuery;
+import org.eclipse.persistence.jpa.jpql.ContentAssistProposals;
+import org.eclipse.persistence.jpa.jpql.ResultQuery;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * @version 3.3
+ * @since 3.3
+ * @author Pascal Filion
+ */
+final class JpqlJavaCompletionProposal extends JpqlCompletionProposal {
+
+ JpqlJavaCompletionProposal(ContentAssistProposals proposals,
+ String proposal,
+ String displayString,
+ String additionalInfo,
+ Image image,
+ NamedQuery namedQuery,
+ String jpqlQuery,
+ int tokenStart,
+ int tokenEnd,
+ int position,
+ int cursorOffset) {
+
+ super(proposals,
+ proposal,
+ displayString,
+ additionalInfo,
+ image,
+ namedQuery,
+ jpqlQuery,
+ tokenStart,
+ tokenEnd,
+ position,
+ cursorOffset);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ ResultQuery buildResult() {
+ return proposals.buildEscapedQuery(
+ jpqlQuery,
+ proposal,
+ position,
+ isCompletionInserts() ^ toggleCompletion
+ );
+ }
+} \ No newline at end of file
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlSseCompletionProposal.java b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlSseCompletionProposal.java
new file mode 100644
index 0000000000..992af926d7
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.jpa.ui/src/org/eclipse/jpt/jpa/ui/internal/jpql/JpqlSseCompletionProposal.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Oracle. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Oracle - initial API and implementation
+ *
+ ******************************************************************************/
+package org.eclipse.jpt.jpa.ui.internal.jpql;
+
+import java.lang.reflect.Method;
+import org.eclipse.jpt.common.utility.internal.StringTools;
+import org.eclipse.jpt.jpa.core.context.NamedQuery;
+import org.eclipse.jpt.jpa.core.jpql.XmlEscapeCharacterConverter;
+import org.eclipse.persistence.jpa.jpql.ContentAssistProposals;
+import org.eclipse.persistence.jpa.jpql.DefaultContentAssistProposals;
+import org.eclipse.persistence.jpa.jpql.ExpressionTools;
+import org.eclipse.persistence.jpa.jpql.ResultQuery;
+import org.eclipse.persistence.jpa.jpql.WordParser;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * @version 3.3
+ * @since 3.3
+ * @author Pascal Filion
+ */
+final class JpqlSseCompletionProposal extends JpqlCompletionProposal {
+
+ private boolean cDATASection;
+ private int actualPosition;
+ private String actualJpqlQuery;
+
+ JpqlSseCompletionProposal(ContentAssistProposals proposals,
+ String proposal,
+ String displayString,
+ String additionalInfo,
+ Image image,
+ NamedQuery namedQuery,
+ String jpqlQuery,
+ String actualJpqlQuery,
+ int tokenStart,
+ int tokenEnd,
+ int position,
+ int actualPosition,
+ int cursorOffset,
+ boolean cDATASection) {
+
+ super(proposals,
+ proposal,
+ displayString,
+ additionalInfo,
+ image,
+ namedQuery,
+ jpqlQuery,
+ tokenStart,
+ tokenEnd,
+ position,
+ cursorOffset);
+
+ this.actualPosition = actualPosition;
+ this.actualJpqlQuery = actualJpqlQuery;
+ this.cDATASection = cDATASection;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ ResultQuery buildResult() {
+
+ // No need to convert the JPQL query into an escaped version (like converting > into &gt;)
+ if (cDATASection) {
+ return proposals.buildQuery(
+ jpqlQuery,
+ proposal,
+ position,
+ isCompletionInserts() ^ toggleCompletion
+ );
+ }
+
+ // TODO: UPDATE ONCE THE NEXT ECLIPSELINK HERMES 2.5 MILESTONE IS AVAILABLE
+ return /*proposals.*/buildXmlQuery(
+ actualJpqlQuery,
+ proposal,
+ actualPosition,
+ isCompletionInserts() ^ toggleCompletion
+ );
+ }
+
+ /**
+ * TODO: TO DELETE ONCE USING THE NEXT ECLIPSELINK HERMES 2.5 MILESTONE
+ */
+ @SuppressWarnings("nls")
+ private ResultQuery buildXmlQuery(String jpqlQuery, String proposal, int position, boolean insert) {
+
+ // Nothing to replace
+ if (ExpressionTools.stringIsEmpty(proposal)) {
+ return proposals.buildQuery(jpqlQuery, StringTools.EMPTY_STRING, position, false);
+ }
+
+ int[] positions = { position };
+
+ // First convert the escape characters into their unicode characters
+ String query = XmlEscapeCharacterConverter.unescape(jpqlQuery, positions);
+
+ // Calculate the start and end positions
+ WordParser wordParser = new WordParser(query);
+ wordParser.setPosition(positions[0]);
+
+ // int[] proposalPositions = buildPositions(wordParser, proposal, insert);
+ int[] proposalPositions;
+ try {
+ Method buildPositionsMethod = DefaultContentAssistProposals.class.getDeclaredMethod("buildPositions", WordParser.class, String.class, boolean.class);
+ buildPositionsMethod.setAccessible(true);
+ proposalPositions = (int[]) buildPositionsMethod.invoke(proposals, wordParser, proposal, insert);
+ }
+ catch (Exception e) {
+ // This is temporary
+ proposalPositions = new int[2];
+ }
+
+ // Escape the proposal
+ proposal = XmlEscapeCharacterConverter.escape(proposal, new int[1]);
+
+ // Adjust the positions so it's in the original JPQL query, which may contain escaped characters
+ XmlEscapeCharacterConverter.reposition(jpqlQuery, proposalPositions);
+
+ // Create the new JPQL query
+ StringBuilder sb = new StringBuilder(jpqlQuery);
+ sb.replace(proposalPositions[0], proposalPositions[1], proposal);
+
+ // And simply create a new ResultQuery object
+ return proposals.buildQuery(sb.toString(), StringTools.EMPTY_STRING, proposalPositions[0] + proposal.length(), false);
+ }
+} \ No newline at end of file

Back to the top