package org.compass.core.mapping;
import org.compass.core.converter.ConversionException;
import org.compass.core.converter.mapping.ResourcePropertyConverter;
/**
* A simple lookup class, for a given path, will provide simple access to
* it's path and value converter. Also supports path escaping ('a.b' or will
* result in a.b and not alias a and resource property b).
*/
public final class ResourcePropertyLookup {
private AliasMapping aliasMapping;
private ResourcePropertyMapping resourcePropertyMapping;
private ResourcePropertyMapping[] resourcePropertyMappings;
private String lookupName;
private String path;
private String dotPathAlias;
private boolean convertOnlyWithDotPath = true;
private CompassMapping compassMapping;
public ResourcePropertyLookup(CompassMapping compassMapping, String name) {
this.compassMapping = compassMapping;
this.lookupName = name;
// the path is escaped, so don't try to look it up
if (name.charAt(0) == '\'' && name.charAt(name.length() - 1) == '\'') {
path = name.substring(1, name.length() - 1);
} else {
int dotIndex = name.indexOf('.');
if (dotIndex != -1) {
dotPathAlias = name.substring(0, dotIndex);
aliasMapping = compassMapping.getAliasMapping(dotPathAlias);
}
this.resourcePropertyMapping = compassMapping.getResourcePropertyMappingByPath(name);
if (resourcePropertyMapping == null) {
path = name;
} else {
path = resourcePropertyMapping.getPath().getPath();
}
resourcePropertyMappings = compassMapping.getResourcePropertyMappingsByPath(path);
// did not find the resource mapping using "dot path", try and see if we can find a global one
if (resourcePropertyMappings != null && resourcePropertyMapping == null) {
resourcePropertyMapping = resourcePropertyMappings[0];
}
}
}
/**
* Perform specialized convert only when dot path is used. Defaults to <code>true</code>.
*
* <p>Sometimes, several meta-data names are used with different converteres. For example
* map to title both a pure String value and also a numeric value. If using dot path
* notation, Compass will narrow down to the specfic converter (for example a.title.title).
* When not using dot path notation, Compass now has two options for conversion. If this
* flag is set to true (and not using dot path notation), Compass will use a converter based
* on the object type. If this flag is set to false, the first mapping is used to convert.
*/
public void setConvertOnlyWithDotPath(boolean convertOnlyWithDotPath) {
this.convertOnlyWithDotPath = convertOnlyWithDotPath;
}
/**
* Returns the analyzer associated with the resource property. <code>null</code> if none
* is configured on the resource property or resource level.
*/
public String getAnalyzer() {
if (resourcePropertyMapping != null) {
if (resourcePropertyMapping.getAnalyzer() != null) {
return resourcePropertyMapping.getAnalyzer();
}
}
return null;
}
/**
* Returns the lookup name used in order to find the meta-data/property name.
*/
public String getLookupName() {
return lookupName;
}
/**
* Returns the alias used if using dot path notation. Returns <code>null</code> if dot path notation
* was not used.
*/
public String getDotPathAlias() {
return dotPathAlias;
}
/**
* Returns the alias mapping if using dot path notation. Returns <code>null</code> if dot path notation
* was not used.
*/
public AliasMapping getAliasMapping() {
return aliasMapping;
}
/**
* Returns the path matching the provided name. The path is the actual name used to store in
* the index.
*/
public String getPath() {
return path;
}
/**
* Returns the property mapping for the provided name. If not using dot path notation, will
* return the first one that match the "meta-data" name within all of Compass mappings.
*/
public ResourcePropertyMapping getResourcePropertyMapping() {
return resourcePropertyMapping;
}
/**
* Returns a list of property mappings for the provided name. When not using "dot path" which
* allows to narrows down to a specific property mapping, and a general meta-data name is used
* (such as title), will return all the property mappings for it within Compass mappings.
*/
public ResourcePropertyMapping[] getResourcePropertyMappings() {
if (resourcePropertyMappings == null && resourcePropertyMapping != null) {
resourcePropertyMappings = new ResourcePropertyMapping[]{resourcePropertyMapping};
}
return resourcePropertyMappings;
}
/**
* Returns <code>true</code> if there is a specific converter that can be used to convert the
* value.
*
* <p>Note, when {@link #setConvertOnlyWithDotPath(boolean)} is set the <code>true</code>, and
* the name passed to the lookup does not contain "dot notation", <code>false</code> will be returned.
*/
public boolean hasSpecificConverter() {
if (dotPathAlias == null && convertOnlyWithDotPath) {
return false;
}
return resourcePropertyMapping != null && resourcePropertyMapping.getConverter() != null;
}
/**
* Returns the String representation of the provided value Object. If {@link #hasSpecificConverter()}
* return <code>true</code>, will use the first mapping definition for the given name in order to
* convert it from Object to String. If it returns false, will use a Converter assigned to the given
* parameter class. If a <code>String</code> is passed, will normalize it using {@link #normalizeString(String)}.
*
* @see org.compass.core.converter.mapping.ResourcePropertyConverter
*/
public String getValue(Object value) {
if (value instanceof String) {
return normalizeString((String) value);
}
ResourcePropertyConverter converter = null;
if (hasSpecificConverter()) {
converter = resourcePropertyMapping.getResourcePropertyConverter();
}
if (converter == null) {
converter = (ResourcePropertyConverter) compassMapping.getConverterLookup().lookupConverter(value.getClass());
}
return converter.toString(value, resourcePropertyMapping);
}
/**
* Returns the Object converted from the String value. If the {@link #hasSpecificConverter()} returns
* <code>true</code>, will use the first mapping definition for the given name in order to conver it
* from String to Object. If it returns <code>false</code>, will use a Converter assigned to the
* given parameter class.
*
* @see org.compass.core.converter.mapping.ResourcePropertyConverter
*/
public Object fromString(String value) {
ResourcePropertyConverter converter;
if (hasSpecificConverter()) {
converter = resourcePropertyMapping.getResourcePropertyConverter();
} else {
converter = (ResourcePropertyConverter) compassMapping.getConverterLookup().lookupConverter(value.getClass());
}
return converter.fromString(value, resourcePropertyMapping);
}
/**
* Tries to normalize the string using {@link #normalizeString(String)}, and if it fails, will
* return the original value.
*/
public String attemptNormalizeString(String value) {
try {
return normalizeString(value);
} catch (ConversionException e) {
return value;
}
}
/**
* Normalizes a given String value to a (hopefully) String value that mathces the one stored in the
* index.
*
* <p>If {@link #hasSpecificConverter()} return <code>false</code> (note {@link #setConvertOnlyWithDotPath(boolean)})
* will simply return the given value.
*
* <p>If the {@link org.compass.core.converter.mapping.ResourcePropertyConverter} states that it should not be used
* for normalization ({@link org.compass.core.converter.mapping.ResourcePropertyConverter#canNormalize()} returns
* <code>false</code>), the provided value will be returned.
*
* <p>If none of the above happens, will convert it
* {@link org.compass.core.converter.mapping.ResourcePropertyConverter#fromString(String, org.compass.core.mapping.ResourcePropertyMapping)}
* and then {@link org.compass.core.converter.mapping.ResourcePropertyConverter#toString(Object, org.compass.core.mapping.ResourcePropertyMapping)}.
*/
public String normalizeString(String value) throws ConversionException {
if (!hasSpecificConverter()) {
return value;
}
ResourcePropertyConverter converter = resourcePropertyMapping.getResourcePropertyConverter();
if (converter == null) {
return value;
}
if (!converter.canNormalize()) {
return value;
}
return converter.toString(converter.fromString(value, resourcePropertyMapping), resourcePropertyMapping);
}
}