Merged HEAD-BUG-FIX (5.0/Cloud) to HEAD (5.0/Cloud)

85067: Merged PLATFORM1 (5.0/Cloud) to HEAD-BUG-FIX (5.0/Cloud)
      84399: Addition of 'special' facetable properties. These aren't Alfresco properties at all, but are just entities that SOLR understands and can facet on. Part of ACE-2639. Examples currently are TAG and Site.
      Note that these are sorted at the top of the facetable properties list and so should always appear in a first result page.
      As part of this, I've refactored the FTL-specific parts of FacetablePropertiesGet to try and extract out the messy bits into a single class, FacetablePropertyFtl.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@85382 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Mark Rogers
2014-09-20 10:43:57 +00:00
parent e8cc740c42
commit 59f8bd1364
3 changed files with 413 additions and 171 deletions

View File

@@ -30,6 +30,10 @@ import java.util.TreeSet;
import org.alfresco.repo.i18n.StaticMessageLookup;
import org.alfresco.repo.search.impl.solr.facet.SolrFacetService.SyntheticPropertyDefinition;
import org.alfresco.repo.web.scripts.facet.FacetablePropertyFTL.FacetablePropertyFTLComparator;
import org.alfresco.repo.web.scripts.facet.FacetablePropertyFTL.SpecialFacetablePropertyFTL;
import org.alfresco.repo.web.scripts.facet.FacetablePropertyFTL.StandardFacetablePropertyFTL;
import org.alfresco.repo.web.scripts.facet.FacetablePropertyFTL.SyntheticFacetablePropertyFTL;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.i18n.MessageLookup;
import org.alfresco.service.namespace.NamespaceException;
@@ -101,7 +105,7 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
// 'synthetic' properties like size and mimetype. See below for more details.
final Map<String, Object> model = new HashMap<>();
final SortedSet<FacetablePropertyFTLModel> facetableProperties;
final SortedSet<FacetablePropertyFTL<?>> facetableProperties;
if (contentClassQName == null)
{
facetableProperties = toFacetablePropertyModel(facetService.getFacetableProperties(), userLocale);
@@ -117,6 +121,10 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
facetableProperties.addAll(toFacetablePropertyModel_(facetableSyntheticProperties, userLocale));
}
// Always add some hard-coded facetable "properties"
facetableProperties.add(new SpecialFacetablePropertyFTL("TAG", "Tag"));
facetableProperties.add(new SpecialFacetablePropertyFTL("SITE", "Site"));
// The webscript allows for some further filtering of results:
List<ResultFilter> filters = new ArrayList<>();
@@ -126,7 +134,7 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
{
filters.add(new ResultFilter()
{
@Override public boolean filter(FacetablePropertyFTLModel facetableProperty)
@Override public boolean filter(FacetablePropertyFTL<?> facetableProperty)
{
final QName propQName = facetableProperty.getQname();
Collection<String> prefixes = namespaceService.getPrefixes(propQName.getNamespaceURI());
@@ -135,7 +143,7 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
});
}
List<FacetablePropertyFTLModel> filteredFacetableProperties = filter(facetableProperties, filters);
List<FacetablePropertyFTL<?>> filteredFacetableProperties = filter(facetableProperties, filters);
if (logger.isDebugEnabled())
{
@@ -154,24 +162,24 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
}
/**
* This type defines the (inclusion) filtering of {@link FacetablePropertyFTLModel property data}
* This type defines the (inclusion) filtering of {@link FacetablePropertyFTL property data}
* in the response to this webscript.
*/
private static interface ResultFilter
{
/** @return {@code true} if the specified property should be included. */
public boolean filter(FacetablePropertyFTLModel facetableProperty);
public boolean filter(FacetablePropertyFTL<?> facetableProperty);
}
/**
* This method returns a new List instance containing only those {@link FacetablePropertyFTLModel property data}
* This method returns a new List instance containing only those {@link FacetablePropertyFTL property data}
* that satisfy all {@link ResultFilter filters}.
*/
private List<FacetablePropertyFTLModel> filter(Collection<FacetablePropertyFTLModel> propsData, List<ResultFilter> filters)
private List<FacetablePropertyFTL<?>> filter(Collection<FacetablePropertyFTL<?>> propsData, List<ResultFilter> filters)
{
final List<FacetablePropertyFTLModel> filteredResult = new ArrayList<>();
final List<FacetablePropertyFTL<?>> filteredResult = new ArrayList<>();
for (FacetablePropertyFTLModel prop : propsData)
for (FacetablePropertyFTL<?> prop : propsData)
{
boolean passedAllFilters = true;
for (ResultFilter filter : filters)
@@ -184,16 +192,16 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
return filteredResult;
}
/** This method returns a {@link FacetablePropertyFTLModel} for the specified {@link PropertyDefinition}. */
private FacetablePropertyFTLModel toFacetablePropertyModel(PropertyDefinition propDef, Locale locale)
/** This method returns a {@link FacetablePropertyFTL} for the specified {@link PropertyDefinition}. */
private FacetablePropertyFTL<?> toFacetablePropertyModel(PropertyDefinition propDef, Locale locale)
{
String title = propDef.getTitle(messageLookup, locale);
return new FacetablePropertyFTLModel(propDef, title);
return new StandardFacetablePropertyFTL(propDef, title);
}
/** This method returns a {@link FacetablePropertyFTLModel} for the specified {@link SyntheticPropertyDefinition}. */
private FacetablePropertyFTLModel toFacetablePropertyModel(SyntheticPropertyDefinition propDef,
Locale locale)
/** This method returns a {@link FacetablePropertyFTL} for the specified {@link SyntheticPropertyDefinition}. */
private FacetablePropertyFTL<?> toFacetablePropertyModel(SyntheticPropertyDefinition propDef,
Locale locale)
{
// Note the hard-coded assumption here that all synthetic properties are defined only
// within the cm:content property type. This code is not designed to be extended.
@@ -203,16 +211,16 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
final String i18nKeyPrefix = "cm_contentmodel.property.cm_content.cm_content.";
final String localisedTitle = I18NUtil.getMessage(i18nKeyPrefix + propDef.syntheticPropertyName, locale);
return new SyntheticFacetablePropertyFTLModel(propDef.containingPropertyDef,
localisedTitle,
propDef.syntheticPropertyName,
propDef.dataTypeDefinition);
return new SyntheticFacetablePropertyFTL(propDef.containingPropertyDef,
localisedTitle,
propDef.syntheticPropertyName,
propDef.dataTypeDefinition);
}
private SortedSet<FacetablePropertyFTLModel> toFacetablePropertyModel(Collection<PropertyDefinition> propDefs,
Locale locale)
private SortedSet<FacetablePropertyFTL<?>> toFacetablePropertyModel(Collection<PropertyDefinition> propDefs,
Locale locale)
{
SortedSet<FacetablePropertyFTLModel> result = new TreeSet<>();
SortedSet<FacetablePropertyFTL<?>> result = new TreeSet<>(new FacetablePropertyFTLComparator());
for (PropertyDefinition propDef : propDefs)
{
result.add(toFacetablePropertyModel(propDef, locale));
@@ -221,153 +229,16 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
}
// Note: the trailing underscore in this method name is to prevent a clash between this method and the
// one that takes a Collection<PropertyDefinition> as type erasure means that both methods would have the
// same signature without the trailing underscore.
private SortedSet<FacetablePropertyFTLModel> toFacetablePropertyModel_(Collection<SyntheticPropertyDefinition> propDefs,
Locale locale)
// one that takes a Collection<PropertyDefinition> as Java's type erasure means that both methods would have the
// same signature, without the trailing underscore.
private SortedSet<FacetablePropertyFTL<?>> toFacetablePropertyModel_(Collection<SyntheticPropertyDefinition> propDefs,
Locale locale)
{
SortedSet<FacetablePropertyFTLModel> result = new TreeSet<>();
SortedSet<FacetablePropertyFTL<?>> result = new TreeSet<>(new FacetablePropertyFTLComparator());
for (SyntheticPropertyDefinition propDef : propDefs)
{
result.add(toFacetablePropertyModel(propDef, locale));
}
return result;
}
/** A simple POJO/DTO intended primarily for use in an FTL model and rendering in the JSON API. */
public static class FacetablePropertyFTLModel implements Comparable<FacetablePropertyFTLModel>
{
/** The Alfresco property definition which declares this facetable property. */
protected final PropertyDefinition propDef;
/** The localised title for this property. */
protected final String localisedTitle;
/** A display name for this property. */
protected String displayName;
/**
* @param propDef The {@link PropertyDefinition}.
* @param localisedTitle The localised title for this property e.g. "Titre".
*/
public FacetablePropertyFTLModel(PropertyDefinition propDef, String localisedTitle)
{
this.propDef = propDef;
this.localisedTitle = localisedTitle;
this.displayName = getShortQname() + (localisedTitle == null ? "" : " (" + localisedTitle + ")");
}
// We use "*Qname*" (small 'n') in these accessors to make the FTL less ambiguous.
public String getShortQname() { return propDef.getName().getPrefixString(); }
public QName getQname() { return propDef.getName(); }
public String getTitle() { return localisedTitle; }
public String getDisplayName() { return displayName; }
public QName getContainerClassType() { return propDef.getContainerClass().getName(); }
public QName getDataType() { return propDef.getDataType().getName(); }
public QName getModelQname() { return propDef.getModel().getName(); }
@Override public boolean equals(Object obj)
{
if (this == obj) { return true; }
if (obj == null) { return false; }
if (getClass() != obj.getClass()) { return false; }
FacetablePropertyFTLModel other = (FacetablePropertyFTLModel) obj;
if (displayName == null)
{
if (other.displayName != null) { return false; }
} else if (!displayName.equals(other.displayName)) { return false; }
if (localisedTitle == null)
{
if (other.localisedTitle != null)
return false;
} else if (!localisedTitle.equals(other.localisedTitle))
return false;
if (propDef == null)
{
if (other.propDef != null)
return false;
} else if (!propDef.equals(other.propDef))
return false;
return true;
}
@Override public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((displayName == null) ? 0 : displayName.hashCode());
result = prime * result + ((localisedTitle == null) ? 0 : localisedTitle.hashCode());
result = prime * result + ((propDef == null) ? 0 : propDef.hashCode());
return result;
}
@Override public int compareTo(FacetablePropertyFTLModel that)
{
final int modelComparison = this.propDef.getModel().getName().compareTo(that.propDef.getModel().getName());
final int classComparison = this.propDef.getContainerClass().getName().compareTo(that.propDef.getContainerClass().getName());
final int propComparison = this.propDef.getName().compareTo(that.propDef.getName());
// this comparison matters because it incorporates SyntheticProperties like size & mimetype. See below.
final int propWithSynthetic = this.getShortQname().compareTo(that.getShortQname());
final int result;
if (modelComparison != 0) { result = modelComparison; }
else if (classComparison != 0) { result = classComparison; }
else if (propComparison != 0) { result = propComparison; }
else { result = propWithSynthetic; }
return result;
}
}
/**
* This class represents a facetable property, which is not actually an Alfresco
* content property. Examples are the {@code size} and {@code MIME type} fields
* within the {@code cm:content} property type.
*/
public static class SyntheticFacetablePropertyFTLModel extends FacetablePropertyFTLModel
{
/** This is the name of the synthetic property e.g. "size". Not localised. */
private final String syntheticPropertyName;
/** The type of this synthetic data property. */
private final QName datatype;
/**
* @param containingPropDef The {@link PropertyDefinition}.
* @param localisedTitle The localised title of this synthetic property e.g. "taille".
* @param syntheticPropertyName The synthetic property name e.g. "size".
* @param datatype The datatype of the synthetic property.
*/
public SyntheticFacetablePropertyFTLModel(PropertyDefinition containingPropDef,
String localisedTitle,
String syntheticPropertyName,
QName datatype)
{
super(containingPropDef, localisedTitle);
this.syntheticPropertyName = syntheticPropertyName;
this.datatype = datatype;
this.displayName = getShortQname() + (localisedTitle == null ? "" : " (" + localisedTitle + ")");
}
@Override public String getShortQname()
{
return super.getShortQname() + "." + this.syntheticPropertyName;
}
@Override public QName getQname()
{
final QName containingPropQName = super.getQname();
return QName.createQName(containingPropQName.getNamespaceURI(),
containingPropQName.getLocalName() + "." + this.syntheticPropertyName);
}
@Override public QName getDataType() { return datatype; }
}
}

View File

@@ -0,0 +1,363 @@
/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.web.scripts.facet;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.namespace.QName;
/**
* This interface defines a simple POJO/DTO for use in the FTL model and rendering in the JSON API.
* @param T a type to ensure that the comparator is implemented in a typesafe way.
* @since 5.0
*/
public abstract class FacetablePropertyFTL<T> implements Comparable<T>
{
/** The localised title for this property. */
protected final String localisedTitle;
public FacetablePropertyFTL(String localisedTitle) { this.localisedTitle = localisedTitle; }
// We use "*Qname*" (small 'n') in these accessors to make the FTL less ambiguous.
public abstract String getShortQname();
public abstract QName getQname();
public abstract String getDisplayName();
public abstract QName getContainerClassType();
public abstract QName getDataType();
public abstract QName getModelQname();
public String getTitle() { return localisedTitle; }
/** This class represents a normal Alfresco property which is facetable. */
public static class StandardFacetablePropertyFTL extends FacetablePropertyFTL<StandardFacetablePropertyFTL>
{
/** The Alfresco property definition which declares this facetable property. */
protected final PropertyDefinition propDef;
/** A display name for this property. */
protected final String displayName;
/**
* @param propDef The {@link PropertyDefinition}.
* @param localisedTitle The localised title for this property e.g. "Titre".
*/
public StandardFacetablePropertyFTL(PropertyDefinition propDef, String localisedTitle)
{
super(localisedTitle);
this.propDef = propDef;
this.displayName = getShortQname() + (localisedTitle == null ? "" : " (" + localisedTitle + ")");
}
@Override public String getShortQname() { return propDef.getName().getPrefixString(); }
@Override public QName getQname() { return propDef.getName(); }
@Override public String getDisplayName() { return displayName; }
@Override public QName getContainerClassType() { return propDef.getContainerClass().getName(); }
@Override public QName getDataType() { return propDef.getDataType().getName(); }
@Override public QName getModelQname() { return propDef.getModel().getName(); }
@Override public boolean equals(Object obj)
{
if (this == obj) { return true; }
if (obj == null) { return false; }
if (getClass() != obj.getClass()) { return false; }
StandardFacetablePropertyFTL other = (StandardFacetablePropertyFTL) obj;
if (displayName == null)
{
if (other.displayName != null) { return false; }
} else if (!displayName.equals(other.displayName)) { return false; }
if (localisedTitle == null)
{
if (other.localisedTitle != null)
return false;
} else if (!localisedTitle.equals(other.localisedTitle))
return false;
if (propDef == null)
{
if (other.propDef != null)
return false;
} else if (!propDef.equals(other.propDef))
return false;
return true;
}
@Override public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((displayName == null) ? 0 : displayName.hashCode());
result = prime * result + ((localisedTitle == null) ? 0 : localisedTitle.hashCode());
result = prime * result + ((propDef == null) ? 0 : propDef.hashCode());
return result;
}
@Override public int compareTo(StandardFacetablePropertyFTL that)
{
final int modelComparison = this.propDef.getModel().getName().compareTo(that.propDef.getModel().getName());
final int classComparison = this.propDef.getContainerClass().getName().compareTo(that.propDef.getContainerClass().getName());
final int propComparison = this.propDef.getName().compareTo(that.propDef.getName());
final int result;
if (modelComparison != 0) { result = modelComparison; }
else if (classComparison != 0) { result = classComparison; }
else { result = propComparison; }
return result;
}
}
/**
* This class represents a facetable property, which is not actually an Alfresco
* content property, but is closely associated with one.
* Examples are the {@code size} and {@code MIME type} fields within the {@code cm:content} property type.
*/
public static class SyntheticFacetablePropertyFTL extends FacetablePropertyFTL<SyntheticFacetablePropertyFTL>
{
/** The PropertyDefinition which 'contains' this synthetic property. */
private final PropertyDefinition containingPropDef;
/** This is the name of the synthetic property e.g. "size". Not localised. */
private final String syntheticPropertyName;
/** The type of this synthetic data property. */
private final QName datatype;
private final String displayName;
/**
* @param containingPropDef The {@link PropertyDefinition}.
* @param localisedTitle The localised title of this synthetic property e.g. "taille".
* @param syntheticPropertyName The synthetic property name e.g. "size".
* @param datatype The datatype of the synthetic property.
*/
public SyntheticFacetablePropertyFTL(PropertyDefinition containingPropDef,
String localisedTitle,
String syntheticPropertyName,
QName datatype)
{
super(localisedTitle);
this.containingPropDef = containingPropDef;
this.syntheticPropertyName = syntheticPropertyName;
this.datatype = datatype;
this.displayName = getShortQname() + (localisedTitle == null ? "" : " (" + localisedTitle + ")");
}
@Override public String getShortQname()
{
return containingPropDef.getName().getPrefixString() + "." + this.syntheticPropertyName;
}
@Override public QName getQname()
{
final QName containingPropQName = containingPropDef.getName();
return QName.createQName(containingPropQName.getNamespaceURI(),
containingPropQName.getLocalName() + "." + this.syntheticPropertyName);
}
@Override public QName getDataType() { return datatype; }
@Override public QName getContainerClassType() { return containingPropDef.getContainerClass().getName(); };
@Override public QName getModelQname() { return containingPropDef.getModel().getName(); }
@Override public String getDisplayName() { return displayName; }
@Override public int compareTo(SyntheticFacetablePropertyFTL that)
{
final int modelComparison = this.containingPropDef.getModel().getName().compareTo(that.containingPropDef.getModel().getName());
final int classComparison = this.containingPropDef.getContainerClass().getName().compareTo(that.containingPropDef.getContainerClass().getName());
final int propComparison = this.containingPropDef.getName().compareTo(that.containingPropDef.getName());
final int displayNameComparison = this.displayName.compareTo(that.displayName);
final int result;
if (modelComparison != 0) { result = modelComparison; }
else if (classComparison != 0) { result = classComparison; }
else if (propComparison != 0) { result = propComparison; }
else { result = displayNameComparison; }
return result;
}
@Override public int hashCode()
{
final int prime = 31;
int result = super.hashCode();
result = prime
* result
+ ((containingPropDef == null) ? 0 : containingPropDef
.hashCode());
result = prime * result
+ ((datatype == null) ? 0 : datatype.hashCode());
result = prime * result
+ ((displayName == null) ? 0 : displayName.hashCode());
result = prime
* result
+ ((syntheticPropertyName == null) ? 0
: syntheticPropertyName.hashCode());
return result;
}
@Override public boolean equals(Object obj)
{
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
SyntheticFacetablePropertyFTL other = (SyntheticFacetablePropertyFTL) obj;
if (containingPropDef == null)
{
if (other.containingPropDef != null)
return false;
} else if (!containingPropDef.equals(other.containingPropDef))
return false;
if (datatype == null)
{
if (other.datatype != null)
return false;
} else if (!datatype.equals(other.datatype))
return false;
if (displayName == null)
{
if (other.displayName != null)
return false;
} else if (!displayName.equals(other.displayName))
return false;
if (syntheticPropertyName == null)
{
if (other.syntheticPropertyName != null)
return false;
} else if (!syntheticPropertyName
.equals(other.syntheticPropertyName))
return false;
return true;
}
}
/**
* This class represents a hard-coded facetable pseudo-property. It is not an Alfresco property
* and yet it is something that Alfresco and SOLR can use for facetting.
* Examples are the {@code TAG} and {@code SITE} facets.
*/
public static class SpecialFacetablePropertyFTL extends FacetablePropertyFTL<SpecialFacetablePropertyFTL>
{
/** This is the name of the property e.g. "SITE". Not localised. */
private final String name;
private final String displayName;
/**
* @param localisedTitle The localised title of this synthetic property e.g. "taille".
*/
public SpecialFacetablePropertyFTL(String name, String localisedTitle)
{
super(localisedTitle);
this.name = name;
this.displayName = localisedTitle;
}
@Override public String getShortQname() { return name; }
@Override public QName getQname() { return null; }
@Override public QName getDataType() { return null; }
@Override public QName getContainerClassType() { return null; }
@Override public QName getModelQname() { return null; }
@Override public String getDisplayName() { return displayName; }
@Override public int compareTo(SpecialFacetablePropertyFTL that)
{
return this.name.compareTo(that.name);
}
@Override public int hashCode()
{
final int prime = 31;
int result = super.hashCode();
result = prime * result
+ ((displayName == null) ? 0 : displayName.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override public boolean equals(Object obj)
{
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
SpecialFacetablePropertyFTL other = (SpecialFacetablePropertyFTL) obj;
if (displayName == null)
{
if (other.displayName != null)
return false;
} else if (!displayName.equals(other.displayName))
return false;
if (name == null)
{
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
/**
* In order to give deterministic responses when getting facetable properties,
* all {@link FacetablePropertyFTL} instances are sorted. This comparator provides
* the sorting implementation.
*/
public static class FacetablePropertyFTLComparator implements Comparator<FacetablePropertyFTL<?>>
{
/** Used when sorting two objects of different types. */
private final List<Class<?>> typeOrder = Arrays.asList(new Class<?>[] { SpecialFacetablePropertyFTL.class,
SyntheticFacetablePropertyFTL.class,
StandardFacetablePropertyFTL.class} );
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override public int compare(FacetablePropertyFTL left, FacetablePropertyFTL right)
{
// First of all sort by the class according to the list above.
if ( !left.getClass().equals(right.getClass())) { return typeOrder.indexOf(left.getClass()) -
typeOrder.indexOf(right.getClass()); }
else
{
// Otherwise we have two facetable properties of the same class.
return left.compareTo(right);
}
}
}
}