/******************************************************************************* * Copyright (c) 2012 Spring IDE Developers * 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 * * Contributors: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.data.jdt.core; import org.eclipse.jdt.core.IField; import org.springframework.data.mapping.PropertyPath; import org.springframework.data.mapping.PropertyReferenceException; import org.springframework.data.repository.query.parser.Part; import org.springframework.data.repository.query.parser.Part.Type; import org.springframework.util.Assert; /** * A part of a Spring Data query method, i.e. exactly the part between the last concatenator ({@literal And}, * {@literal Or}) and the current cursor position. * * @author Oliver Gierke */ class QueryMethodPart { private final String source; private PropertyPath path; private String seed; private Type type; /** * Creates a new {@link QueryMethodPart} from the given source and entity type. * * @param source must not be {@literal null}. * @param domainType must not be {@literal null}. */ public QueryMethodPart(String source, Class<?> domainType) { this.source = source; if (source != null && source.length() > 0) { try { Part part = new Part(source, domainType); this.path = part.getProperty(); this.seed = null; this.type = part.getType(); } catch (PropertyReferenceException e) { this.path = e.getBaseProperty(); this.seed = e.getPropertyName(); this.type = null; } } } /** * Returns whether the part is a complete match, i.e. the part can be resolved into a {@link Part} entirely. * * @return */ public boolean isCompletePathMatch() { return path != null && seed == null; } /** * Return whether the {@link QueryMethodPart} is referencing the root entity. This means no property reference could * be resolved on top of the configured domain type. * * @return */ public boolean isRoot() { return path == null; } /** * Returns whether the part is a complete match including a dedicated keyword. * * @return */ public boolean isKeywordComplete() { if (!isCompletePathMatch()) { return false; } if (!type.equals(Type.SIMPLE_PROPERTY)) { return true; } for (String keyword : Type.SIMPLE_PROPERTY.getKeywords()) { if (source.endsWith(keyword)) { return true; } } return false; } /** * Returns the leaf property of the query method part or the one of the base property in case the part is a partial * match only. * * @return */ public PropertyPath getPathLeaf() { return this.path == null ? null : this.path.getLeafProperty(); } /** * Returns the seed to hint into the next property or keyword. * * @return the seed to hint into the next property or keyword or {@literal null} if none available. */ public String getSeed() { return seed; } /** * Returns the explicit keyword a {@link QueryMethodPart} ends with. * * @return the explicit keyword a {@link QueryMethodPart} ends with or {@literal null} if no explicit keyword is used. */ public String getKeyword() { if (!isCompletePathMatch()) { return null; } PropertyPath current = path; int index = path.getSegment().length(); while (current.hasNext()) { current = current.next(); index += current.getSegment().length(); } return source.length() == index ? null : source.substring(index); } /** * Returns whether the given {@link IField} is a proposal candidate. * * @param field must not be {@literal null}. * @return */ public boolean isProposalCandidate(IField field) { Assert.notNull(field); return seed == null ? true : field.getElementName().startsWith(seed); } }