diff options
Diffstat (limited to 'jpa/plugins/org.eclipse.jpt.jpa.core/src/org/eclipse/jpt/jpa/core')
15 files changed, 1110 insertions, 142 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 >. + */ + 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>&</b>. + */ + public static final String AMPERSAND_ENTITY_NAME = "&"; + + /** + * The entity name for apostrophe: <b>'</b>. + */ + public static final String APOSTROPHE_ENTITY_NAME = "'"; + + /** + * 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>></b>. + */ + public static final String GREATER_THAN_ENTITY_NAME = ">"; + + /** + * The entity name for less-than symbol: <b><</b>. + */ + public static final String LESS_THAN_ENTITY_NAME = "<"; + + /** + * The entity name for quotation mark: <b>"</b>. + */ + public static final String QUOTATION_MARK_NAME = """; + + /** + * 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 � 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>&copy;</b> for <b>©</b></li> + * <li>Numeric character reference (decimal value): <b>&#169;</b> for <b>©</b></li> + * <li>Numeric character reference (hexadecimal value): <b>&#xA9;</b> for <b>©</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 */ |