Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.nuiton.wikitty.storage.WikittySearchEngineInMemory Maven / Gradle / Ivy
/*
* #%L
* Wikitty :: api
* %%
* Copyright (C) 2009 - 2010 CodeLutin, Benjamin Poussin
* %%
* This program 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.
*
* This program 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 General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
package org.nuiton.wikitty.storage;
import com.google.common.collect.Multiset;
import com.google.common.collect.TreeMultiset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Factory;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.wikitty.WikittyException;
import org.nuiton.wikitty.WikittyUtil;
import org.nuiton.wikitty.entities.ElementExtension;
import org.nuiton.wikitty.entities.ElementField;
import org.nuiton.wikitty.entities.ElementId;
import org.nuiton.wikitty.entities.FieldType;
import org.nuiton.wikitty.entities.Wikitty;
import org.nuiton.wikitty.entities.WikittyExtension;
import org.nuiton.wikitty.entities.WikittyTypes;
import org.nuiton.wikitty.query.FacetQuery;
import org.nuiton.wikitty.query.FacetSortType;
import org.nuiton.wikitty.query.FacetTopic;
import org.nuiton.wikitty.query.WikittyQuery;
import org.nuiton.wikitty.query.WikittyQueryResult;
import org.nuiton.wikitty.query.WikittyQueryResultTreeNode;
import org.nuiton.wikitty.query.WikittyQueryVisitor;
import org.nuiton.wikitty.query.conditions.Condition;
import org.nuiton.wikitty.query.conditions.ConditionValue;
import org.nuiton.wikitty.query.conditions.ConditionValueString;
import org.nuiton.wikitty.query.conditions.ContainsAll;
import org.nuiton.wikitty.query.conditions.ContainsOne;
import org.nuiton.wikitty.query.conditions.Equals;
import org.nuiton.wikitty.query.conditions.Greater;
import org.nuiton.wikitty.query.conditions.GreaterOrEquals;
import org.nuiton.wikitty.query.conditions.Less;
import org.nuiton.wikitty.query.conditions.LessOrEquals;
import org.nuiton.wikitty.query.conditions.Like;
import org.nuiton.wikitty.query.conditions.NotEquals;
import org.nuiton.wikitty.query.conditions.NotNull;
import org.nuiton.wikitty.query.conditions.Select;
import org.nuiton.wikitty.query.conditions.Unlike;
import org.nuiton.wikitty.query.function.FunctionValue;
import org.nuiton.wikitty.query.function.WikittyQueryFunction;
import org.nuiton.wikitty.search.Criteria;
import org.nuiton.wikitty.search.PagedResult;
import org.nuiton.wikitty.search.TreeNodeResult;
import org.nuiton.wikitty.search.operators.And;
import org.nuiton.wikitty.search.operators.AssociatedRestriction;
import org.nuiton.wikitty.search.operators.Between;
import org.nuiton.wikitty.search.operators.BinaryOperator;
import org.nuiton.wikitty.search.operators.Contains;
import org.nuiton.wikitty.search.operators.Element;
import org.nuiton.wikitty.search.operators.False;
import org.nuiton.wikitty.search.operators.In;
import org.nuiton.wikitty.search.operators.Keyword;
import org.nuiton.wikitty.search.operators.Not;
import org.nuiton.wikitty.search.operators.Null;
import org.nuiton.wikitty.search.operators.Or;
import org.nuiton.wikitty.search.operators.Restriction;
import org.nuiton.wikitty.search.operators.RestrictionName;
import org.nuiton.wikitty.search.operators.True;
import org.nuiton.wikitty.services.WikittyTransaction;
public class WikittySearchEngineInMemory implements WikittySearchEngine {
/** to use log facility, just put in your code: log.info(\"...\"); */
static private Log log = LogFactory.getLog(WikittySearchEngineInMemory.class);
protected WikittyStorageInMemory wikittyStorage;
public WikittySearchEngineInMemory(WikittyStorageInMemory wikittyStorage) {
this.wikittyStorage = wikittyStorage;
}
@Override
public void clear(WikittyTransaction transaction) {
// do nothing
}
@Override
public void store(WikittyTransaction transaction, Collection wikitties, boolean force) {
// do nothing
}
@Override
public void delete(WikittyTransaction transaction, Collection idList) throws WikittyException {
// do nothing
}
static private boolean checkRestriction(WikittySearchEngine searchEngine,
WikittyTransaction transaction, Condition condition, Wikitty w) {
WikittyQueryVisitorCheckCondition v =
new WikittyQueryVisitorCheckCondition(searchEngine, transaction, w);
condition.accept(v);
boolean result = v.getResult();
return result;
}
/** comparateur generic qui accept deux objets de meme type en argument */
static public Comparator genericComparator = new Comparator() {
public int compare(Object v1, Object v2) {
if (v1 == null && v2 == null) {
return 0;
} else if (v1 == null) {
return -1;
} else if (v2 == null) {
return 1;
}
if (v1 == v2 || v1.equals(v2)) {
return 0;
}
if (v1 instanceof Collection) {
v1 = CollectionUtils.get(v1, 0);
}
if (v2 instanceof Collection) {
v2 = CollectionUtils.get(v2, 0);
}
Comparable c1;
Comparable c2;
if (v1 instanceof Comparable) {
c1 = (Comparable)v1;
} else {
return 0; // non comparable on retourne 0
}
if (v2 instanceof Comparable) {
c2 = (Comparable)v2;
} else {
return 0; // non comparable on retourne 0
}
int result = c1.compareTo(c2);
return result;
}
};
static public class WikittyComparator implements Comparator {
protected List asc;
protected List desc;
public WikittyComparator(List asc, List desc) {
this.asc = asc;
this.desc = desc;
}
/** compare un champs de deux objets Wikitty */
protected int compareValue(Wikitty o1, Wikitty o2, org.nuiton.wikitty.entities.Element e, boolean asc) {
Object v1 = o1.getFqField(e.getValue());
Object v2 = o2.getFqField(e.getValue());
int result = genericComparator.compare(v1, v2);
if (!asc) {
result = result * -1;
}
return result;
}
public int compare(Wikitty o1, Wikitty o2) {
int result = 0;
for (org.nuiton.wikitty.entities.Element e : asc) {
result = compareValue(o1, o2, e, true);
if (result != 0) {
return result;
}
}
for (org.nuiton.wikitty.entities.Element e : desc) {
result = compareValue(o1, o2, e, false);
if (result != 0) {
return result;
}
}
return result;
}
}
static public class FacetPredicate {
WikittyTransaction tx;
WikittySearchEngine searchEngine;
WikittyQuery query;
Map topic;
public FacetPredicate(WikittySearchEngine searchEngine,
WikittyTransaction tx, WikittyQuery query) {
this.searchEngine = searchEngine;
this.tx = tx;
this.query = query;
topic = LazyMap.decorate(new HashMap(), new Factory() {
public Object create() {
return TreeMultiset.create(genericComparator);
}
});
}
public Map> getFacets() {
Map> result = new HashMap>();
for (String facetName : topic.keySet()) {
List list = new ArrayList();
Multiset b = topic.get(facetName);
for (Object topicName : b.elementSet()) {
int count = b.count(topicName);
// pour ajouter le topic il faut un minimum indique dans la query
if (count >= query.getFacetMinCount()) {
FacetTopic ft = new FacetTopic(facetName, String.valueOf(topicName), count);
list.add(ft);
}
}
// on re-tri si besoin, sinon l'ordre par defaut est sur la valeur des topics
if (query.getFacetSort() == FacetSortType.count) {
Collections.sort(list, query.getFacetSort().compartor);
}
// ... et on en prend que le nombre demande
if (list.size() > query.getFacetLimit()) {
list = list.subList(0, query.getFacetLimit());
}
result.put(facetName, list);
}
return result;
}
public boolean add(Wikitty w) {
boolean result = false;
// create facet extension
if (query.isFacetExtension()) {
String facetName = ElementExtension.EXTENSION.getValue();
for (String extName : w.getExtensionNames()) {
topic.get(facetName).add(extName);
result = true;
}
}
// create facet field
for (org.nuiton.wikitty.entities.Element e : query.getFacetField()) {
if (ElementExtension.EXTENSION.equals(e)) {
String facetName = ElementExtension.EXTENSION.getValue();
for (String extName : w.getExtensionNames()) {
topic.get(facetName).add(extName);
result = true;
}
} else {
String fqf = e.getValue();
Object value = w.getFqField(fqf);
if (value != null) {
if (value instanceof Collection) {
topic.get(fqf).addAll((Collection)value);
} else {
topic.get(fqf).add(value);
}
result = true;
}
}
}
// create facet query
for (FacetQuery q : query.getFacetQuery()) {
if (checkRestriction(searchEngine, tx, q.getCondition(), w)) {
String facetName = q.getName();
if (facetName == null) {
facetName = q.getCondition().toString();
}
topic.get(facetName).add(facetName);
result = true;
}
}
return result;
}
}
@Override
public WikittyQueryResult> findAllByQuery(WikittyTransaction transaction, WikittyQuery query) {
// la condition select du query, sera traite a la fin
Select select = null;
WikittyQuery queryWithoutSelect = query;
if (query.isSelectQuery()) {
select = query.getSelect();
// copy de la query sans le select
queryWithoutSelect = query.getWhereQuery();
if (queryWithoutSelect.getCondition() == null) {
queryWithoutSelect.setCondition(new org.nuiton.wikitty.query.conditions.True());
}
}
int offset = queryWithoutSelect.getOffset();
int limit = queryWithoutSelect.getLimit();
List ids = new LinkedList();
FacetPredicate facets = new FacetPredicate(this, transaction, queryWithoutSelect);
ArrayList wikitties =
new ArrayList(wikittyStorage.getWikitties().values());
// on tri les wikitties selon l'ordre demande ...
if (!queryWithoutSelect.getSortAscending().isEmpty()
|| !queryWithoutSelect.getSortDescending().isEmpty() ) {
Collections.sort(wikitties, new WikittyComparator(
queryWithoutSelect.getSortAscending(), queryWithoutSelect.getSortDescending()));
}
int totalResult = 0;
for (Wikitty w : wikitties) {
String id = w.getWikittyId();
Condition c = queryWithoutSelect.getCondition();
if (!w.isDeleted()) {
if (checkRestriction(this, transaction, c, w)) {
totalResult++;
if (totalResult > offset && ids.size() < limit) {
// ajout en tant que resultat
ids.add(id);
}
facets.add(w);
}
}
}
List> values = new ArrayList>(ids.size());
List> selectResult = null;
if (select == null) {
String idTag = org.nuiton.wikitty.entities.Element.ID.getValue();
for (Object id : ids) {
values.add(WikittyUtil.singletonMap(idTag, id));
}
} else {
// Extract data
for (String id : ids) {
Wikitty w = wikittyStorage.getWikitties().get(id);
Map map = new LinkedHashMap(w.getFieldValue());
map.put(org.nuiton.wikitty.entities.Element.ID.getValue(), id);
values.add(map);
}
values = select.getFunction().call(query, values);
selectResult = values;
}
WikittyQueryResult> result =
new WikittyQueryResult>(
queryWithoutSelect.getName(), offset, totalResult,
query, queryWithoutSelect.getCondition().toString(),
values, selectResult, ids, facets.getFacets(), 0, 0);
return result;
}
@Override
public WikittyQueryResultTreeNode findAllChildrenCount(WikittyTransaction transaction,
String wikittyId, int depth, boolean count, WikittyQuery filter) {
// FIXME
throw new UnsupportedOperationException("Not supported yet.");
}
static public class WikittyQueryVisitorCheckCondition extends WikittyQueryVisitor {
protected WikittySearchEngine searchEngine;
/** transaction used to check wikitty */
protected WikittyTransaction tx;
/** wikitty to check */
protected Wikitty w;
/** la pile d'evaluation des differencetes contraintes */
protected Deque evalStack = new LinkedList();
public boolean getResult() {
Boolean result = evalStack.poll();
if (result == null) {
// s'il n'y avait pas de condition, la stack est vide, donc
// c'est vrai
result = Boolean.TRUE;
}
return result;
}
public WikittyQueryVisitorCheckCondition(
WikittySearchEngine searchEngine, WikittyTransaction tx, Wikitty w) {
this.searchEngine = searchEngine;
this.tx = tx;
this.w = w;
}
/**
* Interface permettant de verifier une condition sur deux collections
*/
static private interface Predicate {
/**
* retourne vrai si la condition est validee
* @param type type des elements des collections. Si null alors les
* collection contiennent des String (id ou nom extension)
* @param value l'ensemble des valeur du champs
* @param expected l'ensemble des valeurs attendue
* @return vrai si la condition est validee
*/
boolean check(FieldType type, Collection values, Collection expected);
}
static private Predicate BetweenPredicate = new Predicate() {
public boolean check(FieldType type, Collection values, Collection expected) {
boolean result = false;
if (values != null && expected.size() == 2) {
// si on a pas deux valeur, c'est qu'on a pas min et max
Iterator i = expected.iterator();
Object min = i.next();
Object max = i.next();
for (Object fieldValue : values) {
// fieldValue doit etre comparable
if (fieldValue instanceof Comparable) {
// recupere le type de la valeur
result = ((Comparable) fieldValue).compareTo(min) >= 0
&& ((Comparable) fieldValue).compareTo(max) <= 0;
if (result) {
// si une des valeurs correspond, on retourne true
break;
}
}
}
}
return result;
}
};
static private Predicate ContainsAllPredicate = new Predicate() {
public boolean check(FieldType type, Collection values, Collection expected) {
boolean result = false;
// si expected est vide alors rien ne doit matcher
// http://www.nuiton.org/issues/3735
// Anomalie #3735: containsOne and containsAll with an empty collection returns all objects but no object should have been returned
if (values != null && CollectionUtils.isNotEmpty(expected)) {
expected = CollectionUtils.subtract(expected, values);
// si lorsqu'on retire tous les elements en commun avec la valeur
// du champs il ne reste plus rien, c'est que le containsAll est
// vrai
result = expected.isEmpty();
}
return result;
}
};
static private Predicate ContainsOnePredicate = new Predicate() {
public boolean check(FieldType type, Collection values, Collection expected) {
boolean result = false;
if (values != null) {
expected = CollectionUtils.retainAll(expected, values);
// si lorsqu'on retient tous les elements en commun avec la valeur
// du champs il ne reste plus rien, c'est que le containsOne est
// faux, donc il faut qu'il en reste
result = !expected.isEmpty();
}
return result;
}
};
static private Predicate EqualsPredicate = new Predicate() {
public boolean check(FieldType type, Collection values, Collection expected) {
boolean result = false;
if (values != null && expected.size() > 0) {
// gestion des type STRING differement car il peut y avoir des '*'
if (type != null && type.getType() == WikittyTypes.STRING) {
Iterator i = expected.iterator();
String exp = String.valueOf(i.next());
for (Object fieldValue : values) {
String val = String.valueOf(fieldValue);
result = matchString(val, exp, false);
if (result) {
// si une des valeurs correspond, on retourne true
break;
}
}
} else {
expected = CollectionUtils.subtract(expected, values);
// si lorsqu'on retire tous les elements en commun avec la valeur
// du champs il ne reste plus rien, c'est que le equals est
// vrai
result = expected.isEmpty();
}
}
return result;
}
};
static private Predicate EqualsIgnoreCaseAndAccentPredicate = new Predicate() {
public boolean check(FieldType type, Collection values, Collection expected) {
boolean result = false;
if (values != null && expected.size() > 0) {
// gestion des type STRING differement car il peut y avoir des '*'
if (type != null && type.getType() == WikittyTypes.STRING) {
Iterator i = expected.iterator();
String exp = String.valueOf(i.next());
for (Object fieldValue : values) {
String val = String.valueOf(fieldValue);
result = matchString(val, exp, true);
if (result) {
// si une des valeurs correspond, on retourne true
break;
}
}
} else {
expected = CollectionUtils.subtract(expected, values);
// si lorsqu'on retire tous les elements en commun avec la valeur
// du champs il ne reste plus rien, c'est que le equals est
// vrai
result = expected.isEmpty();
}
}
return result;
}
};
static private Predicate GreaterPredicate = new Predicate() {
public boolean check(FieldType type, Collection values, Collection expected) {
boolean result = false;
if (values != null && expected.size() > 0) {
// si on a pas une valeur, c'est pas bon :(
Iterator i = expected.iterator();
Object val = i.next();
for (Object fieldValue : values) {
// fieldValue doit etre comparable
if (fieldValue instanceof Comparable) {
// recupere le type de la valeur
result = ((Comparable) fieldValue).compareTo(val) > 0;
if (result) {
// si une des valeurs correspond, on retourne true
break;
}
}
}
}
return result;
}
};
static private Predicate GreaterOrEqualsPredicate = new Predicate() {
public boolean check(FieldType type, Collection values, Collection expected) {
boolean result = false;
if (values != null && expected.size() > 0) {
// si on a pas une valeur, c'est pas bon :(
Iterator i = expected.iterator();
Object val = i.next();
for (Object fieldValue : values) {
// fieldValue doit etre comparable
if (fieldValue instanceof Comparable) {
// recupere le type de la valeur
result = ((Comparable) fieldValue).compareTo(val) >= 0;
if (result) {
// si une des valeurs correspond, on retourne true
break;
}
}
}
}
return result;
}
};
static private Predicate LessPredicate = new Predicate() {
public boolean check(FieldType type, Collection values, Collection expected) {
boolean result = false;
if (values != null && expected.size() > 0) {
// si on a pas une valeur, c'est pas bon :(
Iterator i = expected.iterator();
Object val = i.next();
for (Object fieldValue : values) {
// fieldValue doit etre comparable
if (fieldValue instanceof Comparable) {
// recupere le type de la valeur
result = ((Comparable) fieldValue).compareTo(val) < 0;
if (result) {
// si une des valeurs correspond, on retourne true
break;
}
}
}
}
return result;
}
};
static private Predicate LessOrEqualsPredicate = new Predicate() {
public boolean check(FieldType type, Collection values, Collection expected) {
boolean result = false;
if (values != null && expected.size() > 0) {
// si on a pas une valeur, c'est pas bon :(
Iterator i = expected.iterator();
Object val = i.next();
for (Object fieldValue : values) {
// fieldValue doit etre comparable
if (fieldValue instanceof Comparable) {
// recupere le type de la valeur
result = ((Comparable) fieldValue).compareTo(val) <= 0;
if (result) {
// si une des valeurs correspond, on retourne true
break;
}
}
}
}
return result;
}
};
static private Predicate KeywordPredicate = new Predicate() {
public boolean check(FieldType type, Collection values, Collection expected) {
boolean result = false;
if (values != null && expected.size() > 0) {
// si on a pas une valeur, c'est pas bon :(
Iterator i = expected.iterator();
String exp = String.valueOf(i.next());
for (Object fieldValue : values) {
String val = String.valueOf(fieldValue);
result = matchString(val, exp, true);
if (result) {
// si une des valeurs correspond, on retourne true
break;
}
}
}
return result;
}
};
static private Predicate LikePredicate = new Predicate() {
public boolean check(FieldType type, Collection values, Collection expected) {
boolean result = false;
if (values != null && expected.size() > 0) {
// si on a pas une valeur, c'est pas bon :(
Iterator i = expected.iterator();
String exp = String.valueOf(i.next());
for (Object fieldValue : values) {
String val = String.valueOf(fieldValue);
result = matchString(val, exp, true);
if (result) {
// si une des valeurs correspond, on retourne true
break;
}
}
}
return result;
}
};
static private Predicate NullPredicate = new Predicate() {
public boolean check(FieldType type, Collection values, Collection expected) {
boolean result = values == null || values.contains(null);
return result;
}
};
static private Predicate NotNullPredicate = new Predicate() {
public boolean check(FieldType type, Collection values, Collection expected) {
boolean result = values != null && !values.contains(null);
return result;
}
};
/**
* check if string match other string.
*
* @param s string
* @param sub string to match with 's' parameter. This string can start
* or and with star '*'
* @param ignoreCaseAndAccent if true match is done in ingore case mode
* @return true if sub match s
*/
static private boolean matchString(String s, String sub, boolean ignoreCaseAndAccent) {
if (ignoreCaseAndAccent) {
s = StringUtils.stripAccents(s);
s = s.toLowerCase();
sub = StringUtils.stripAccents(sub);
sub = sub.toLowerCase();
}
sub = sub.replaceAll("\\*", ".*");
sub = sub.replaceAll("\\?", ".");
boolean result = s.matches(sub);
return result;
}
/**
* Collecte les valeur possible de l'element demande
*
* @param e l'element demande. Peut etre l'id, l'extension ou un champs.
* le champs pouvant contenir des * pour remplacer le nom de extension
* ou le nom du champs (ex: *.name, myext.*)
* @param values une map avec en cle le nom du champs et en valeur
* la valeur du champs
*/
protected void collectFieldValue(
org.nuiton.wikitty.entities.Element e,
Map values) {
if (e instanceof ElementId) {
values.put(e.getValue(), Collections.singleton(w.getWikittyId()));
} else if (e instanceof ElementExtension) {
values.put(e.getValue(), w.getExtensionNames());
} else {
String fqf = e.getValue();
if (w.hasField(fqf)) {
Object v = w.getFqField(fqf);
values.put(fqf, convert(null, v));
} else {
String extName = WikittyExtension.extractExtensionName(fqf);
String fieldName = WikittyExtension.extractFieldName(fqf);
if ("*".equals(extName) && "*".equals(fieldName)) {
// on ajoute tous les champs de toutes les extensions
for (String f : w.getAllFieldNames()) {
Object v = w.getFqField(f);
values.put(f, convert(null, v));
}
} else if ("*".equals(fieldName)){
// on ajoute tous les champs de l'extension demandee
for (String f : w.getExtension(extName).getFieldNames()) {
String fq = WikittyUtil.getFQFieldName(extName, f);
Object v = w.getFqField(fq);
values.put(fq, convert(null, v));
}
} else if ("*".equals(extName)){
// on ajoute tous les champs ayant le nom demande
// quelque soit l'extension
for (String ext : w.getExtensionNames()) {
for (String f : w.getExtension(ext).getFieldNames()) {
if (f.equals(fieldName)) {
String fq = WikittyUtil.getFQFieldName(ext, f);
Object v = w.getFqField(fq);
values.put(fq, convert(null, v));
}
}
}
}
}
}
}
/**
* Converti o en une list de valeur compatible avec type
* @param type le type dans lequel doit etre converti o
* @param o la valeur a convertir, si o est une collection, chaque
* element de la collection est converti
*
* @return une nouvelle collection avec les elements dans le bon type
*/
protected Collection convert(FieldType type, Object o) {
Collection result;
try {
if (o instanceof Collection) {
// order of collection must be maintained, for that use LinkedHashSet
if (type == null) {
// if type is null don't change type (in case of id or extension
// or when we know object is already in right type)
result = new LinkedHashSet((Collection)o);
} else {
result = new LinkedHashSet();
for (Object v : (Collection)o) {
result.add(type.getContainedValidObject(v));
}
}
} else {
if (type != null) {
// if type is null don't change type (in case of id or extension
// or when we know object is already in right type)
o = type.getContainedValidObject(o);
}
result = Collections.singleton(o);
}
} catch (Exception eee) {
// si on arrive pas a convertir on retourne null
result = null;
if (log.isTraceEnabled()) {
log.trace("not an error but can't convert string to wanted type", eee);
}
}
return result;
}
/**
* Verifie qu'un predicat est vrai pour un element et une valeur attendu
* @param predicate le predicat a verifier
* @param element l'element mis en cause (id, extension, field)
* @param expected la/les valeur(s) attendu(s)
* @return vrai si le predicat est verifie
*/
protected boolean check(Predicate predicate,
org.nuiton.wikitty.entities.Element element, Object expected) {
if (element instanceof ElementField) {
String fqf = element.getValue();
// suppression du type dans l'element, car on en a pas besoin
String extName = WikittyUtil.getExtensionNameFromFQFieldName(fqf);
String fieldName = WikittyUtil.getFieldNameFromFQFieldName(fqf);
// on reconstruit fqf, sans l'optionnel 3eme composantes (Type)
fqf = WikittyUtil.getFQFieldName(extName, fieldName);
element = org.nuiton.wikitty.entities.Element.get(fqf);
}
boolean result = false;
boolean isIndexed = true;
Map fieldValues = new HashMap();
collectFieldValue(element, fieldValues);
for (String fqf : fieldValues.keySet()) {
FieldType type = null;
if (w.hasField(fqf)) {
type = w.getFieldType(fqf);
isIndexed = type.isIndexed();
}
Collection contained = convert(type, expected);
if (isIndexed && contained != null) {
// si on a reussi a convertir dans le bon type on fait la verif
Collection values;
values = fieldValues.get(fqf);
result = predicate.check(type, values, contained);
if (result) {
// on a reussi a verifie le predicat pour au moins un champs
// on renvoie donc true
break;
}
}
}
return result;
}
@Override
public void visit(ConditionValueString o) {
// do nothing
}
@Override
public boolean visitEnter(WikittyQuery o) {
// nothing to do
return true;
}
@Override
public void visitLeave(WikittyQuery o, boolean enterOrMiddleResult) {
// nothing to do
}
@Override
public boolean visitEnter(org.nuiton.wikitty.query.conditions.And o) {
evalStack.push(Boolean.TRUE);
return true;
}
@Override
public boolean visitMiddle(org.nuiton.wikitty.query.conditions.And o) {
// l'evaluation du dernier element du and
Boolean last = evalStack.pop();
// la valeur courante du and
Boolean currentValue = evalStack.pop();
Boolean result = currentValue && last;
evalStack.push(result);
// si le and est deja faut ca ne sert a rien de d'evaluer la suite
return result;
}
@Override
public void visitLeave(org.nuiton.wikitty.query.conditions.And o, boolean enterOrMiddleResult) {
Boolean last = Boolean.TRUE;
if (enterOrMiddleResult && o.getConditions().size()>0) {
// si enter ou middle on renvoye false, alors on a pas
// de nouvelle condition a prendre dans la pile sinon
// l'evaluation du dernier element du and
last = evalStack.pop();
}
// la valeur courante du and
Boolean currentValue = evalStack.pop();
Boolean result = currentValue && last;
evalStack.push(result);
}
@Override
public boolean visitEnter(org.nuiton.wikitty.query.conditions.Or o) {
// si le or n'a aucun element, alors il est vrai
if (o.getConditions().isEmpty()) {
evalStack.push(Boolean.TRUE);
} else {
// sinon, il faut qu'un de ses elements soit vrai pour etre vrai
evalStack.push(Boolean.FALSE);
}
return true;
}
@Override
public boolean visitMiddle(org.nuiton.wikitty.query.conditions.Or o) {
// l'evaluation du dernier element du or
Boolean last = evalStack.pop();
// la valeur courante du or
Boolean currentValue = evalStack.pop();
Boolean result = currentValue || last;
evalStack.push(result);
// si le or est deja vrai, ca ne sert a rien d'evaluer la suite
return !result;
}
@Override
public void visitLeave(org.nuiton.wikitty.query.conditions.Or o, boolean enterOrMiddleResult) {
Boolean last = Boolean.TRUE;
if (enterOrMiddleResult && o.getConditions().size()>0) {
// si enter ou middle on renvoye false, alors on a pas
// de nouvelle condition a prendre dans la pile sinon
// l'evaluation du dernier element du or
last = evalStack.pop();
}
// la valeur courante du or
Boolean currentValue = evalStack.pop();
Boolean result = currentValue || last;
evalStack.push(result);
}
@Override
public boolean visitEnter(Select o) {
// do nothing
return true;
}
@Override
public boolean visitMiddle(Select o) {
// do nothing
return true;
}
@Override
public void visitLeave(Select o, boolean enterOrMiddleResult) {
// do nothing
}
protected List evalConditionValueAsList(List o) {
List result = new ArrayList(o.size());
for (ConditionValue c : o) {
result.addAll(evalConditionValueAsList(c));
}
return result;
}
protected List evalConditionValueAsList(ConditionValue o) {
List result = new ArrayList();
if (o instanceof Select) {
WikittyQuery query = new WikittyQuery(o);
// eval select
WikittyQueryResult selectResult =
searchEngine.findAllByQuery(tx, query).convertMapToSimpleString();
result.addAll(selectResult.getAll());
} else if (o instanceof ConditionValueString) {
result.add(((ConditionValueString)o).getValue());
} else {
throw new WikittyException(String.format(
"ConditionValue type unsupported %s",
ClassUtils.getShortCanonicalName(o, "null")));
}
return result;
}
protected String evalConditionValue(ConditionValue o) {
String result;
if (o instanceof Select) {
WikittyQuery query = new WikittyQuery(o);
// eval select
WikittyQueryResult selectResult =
searchEngine.findAllByQuery(tx, query).convertMapToSimpleString();
if (selectResult.size() == 0) {
throw new WikittyException(String.format(
"Select return no result query was '%s' transformed to '%s'",
o.toString(),
selectResult.getQueryString()));
} else if (selectResult.size() > 1) {
if (log.isWarnEnabled()) {
log.warn(String.format(
"Select return more than one result, only first"
+ " is used. Query was '%s' transformed to '%s'",
o.toString(),
selectResult.getQueryString()));
}
}
result = selectResult.peek();
} else if (o instanceof ConditionValueString) {
result = ((ConditionValueString)o).getValue();
} else {
throw new WikittyException(String.format(
"ConditionValue type unsupported %s",
ClassUtils.getShortCanonicalName(o, "null")));
}
return result;
}
@Override
public boolean visitEnter(org.nuiton.wikitty.query.conditions.Not o) {
// nothing to do
return true;
}
@Override
public void visitLeave(org.nuiton.wikitty.query.conditions.Not o, boolean enterOrMiddleResult) {
Boolean val = evalStack.pop();
Boolean result = !val;
evalStack.push(result);
}
@Override
public boolean visitEnter(org.nuiton.wikitty.query.conditions.Between o) {
boolean result = false;
Collection expected = new ArrayList(2);
expected.add(evalConditionValue(o.getMin()));
expected.add(evalConditionValue(o.getMax()));
result = check(BetweenPredicate, o.getElement(), expected);
evalStack.push(result);
return false;
}
@Override
public boolean visitMiddle(org.nuiton.wikitty.query.conditions.Between o) {
// do nothing
return false;
}
@Override
public void visitLeave(org.nuiton.wikitty.query.conditions.Between o, boolean enterOrMiddleResult) {
// do nothing
}
@Override
public boolean visitEnter(ContainsAll o) {
boolean result = false;
result = check(ContainsAllPredicate, o.getElement(),
evalConditionValueAsList(o.getValues()));
evalStack.push(result);
return false;
}
@Override
public boolean visitMiddle(ContainsAll o) {
// do nothing
return false;
}
@Override
public void visitLeave(ContainsAll o, boolean enterOrMiddleResult) {
// do nothing
}
@Override
public boolean visitEnter(ContainsOne o) {
boolean result = false;
result = check(ContainsOnePredicate, o.getElement(),
evalConditionValueAsList(o.getValues()));
evalStack.push(result);
return false;
}
@Override
public boolean visitMiddle(ContainsOne o) {
// do nothing
return false;
}
@Override
public void visitLeave(ContainsOne o, boolean enterOrMiddleResult) {
// do nothing
}
@Override
public boolean visitEnter(Equals o) {
boolean result = false;
if (o.isIgnoreCaseAndAccent()) {
result = check(EqualsIgnoreCaseAndAccentPredicate, o.getElement(),
evalConditionValue(o.getValue()));
} else {
result = check(EqualsPredicate, o.getElement(),
evalConditionValue(o.getValue()));
}
evalStack.push(result);
return false;
}
@Override
public void visitLeave(Equals o, boolean enterOrMiddleResult) {
// do nothing
}
@Override
public boolean visitEnter(NotEquals o) {
boolean result = false;
if (o.isIgnoreCaseAndAccent()) {
result = !check(EqualsIgnoreCaseAndAccentPredicate, o.getElement(),
evalConditionValue(o.getValue()));
} else {
result = !check(EqualsPredicate, o.getElement(),
evalConditionValue(o.getValue()));
}
evalStack.push(result);
return false;
}
@Override
public void visitLeave(NotEquals o, boolean enterOrMiddleResult) {
// do nothing
}
@Override
public void visit(org.nuiton.wikitty.query.conditions.False o) {
evalStack.push(Boolean.FALSE);
}
@Override
public void visit(org.nuiton.wikitty.query.conditions.True o) {
evalStack.push(Boolean.TRUE);
}
@Override
public boolean visitEnter(Greater o) {
boolean result = false;
result = check(GreaterPredicate, o.getElement(),
evalConditionValue(o.getValue()));
evalStack.push(result);
return false;
}
@Override
public void visitLeave(Greater o, boolean enterOrMiddleResult) {
// do nothing
}
@Override
public boolean visitEnter(GreaterOrEquals o) {
boolean result = false;
result = check(GreaterOrEqualsPredicate, o.getElement(),
evalConditionValue(o.getValue()));
evalStack.push(result);
return false;
}
@Override
public void visitLeave(GreaterOrEquals o, boolean enterOrMiddleResult) {
// do nothing
}
@Override
public boolean visitEnter(org.nuiton.wikitty.query.conditions.Keyword o) {
boolean result = false;
result = check(KeywordPredicate, o.getElement(),
"*" + evalConditionValue(o.getValue()) + "*");
evalStack.push(result);
return false;
}
@Override
public void visitLeave(org.nuiton.wikitty.query.conditions.Keyword o, boolean enterOrMiddleResult) {
// do nothing
}
@Override
public boolean visitEnter(Less o) {
boolean result = false;
result = check(LessPredicate, o.getElement(),
evalConditionValue(o.getValue()));
evalStack.push(result);
return false;
}
@Override
public void visitLeave(Less o, boolean enterOrMiddleResult) {
// do nothing
}
@Override
public boolean visitEnter(LessOrEquals o) {
boolean result = false;
result = check(LessOrEqualsPredicate, o.getElement(),
evalConditionValue(o.getValue()));
evalStack.push(result);
return false;
}
@Override
public void visitLeave(LessOrEquals o, boolean enterOrMiddleResult) {
boolean result = false;
result = check(LessOrEqualsPredicate, o.getElement(),
evalConditionValue(o.getValue()));
evalStack.push(result);
}
@Override
public boolean visitEnter(Like o) {
boolean result = false;
result = check(LikePredicate, o.getElement(),
evalConditionValue(o.getValue()));
evalStack.push(result);
return false;
}
@Override
public void visitLeave(Like o, boolean enterOrMiddleResult) {
// do nothing
}
@Override
public boolean visitEnter(Unlike o) {
boolean result = false;
result = !check(LikePredicate, o.getElement(),
evalConditionValue(o.getValue()));
evalStack.push(result);
return false;
}
@Override
public void visitLeave(Unlike o, boolean enterOrMiddleResult) {
// do nothing
}
@Override
public void visit(org.nuiton.wikitty.query.conditions.Null o) {
boolean result = false;
result = check(NullPredicate, o.getElement(), null);
evalStack.push(result);
}
@Override
public void visit(NotNull o) {
boolean result = false;
result = check(NotNullPredicate, o.getElement(), null);
evalStack.push(result);
}
@Override
public boolean visitEnter(WikittyQueryFunction function) {
// do nothing
return true;
}
@Override
public boolean visitMiddle(WikittyQueryFunction function) {
// do nothing
return true;
}
@Override
public void visitLeave(WikittyQueryFunction function, boolean enterOrMiddleResult) {
// do nothing
}
@Override
public void visit(FunctionValue function) {
// do nothing
}
@Override
public void defaultVisit(Object o) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean defaultVisitEnter(Object o) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean defaultVisitMiddle(Object o) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void defaultVisitLeave(Object o, boolean enterOrMiddleResult) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
@Override
public PagedResult findAllByCriteria(WikittyTransaction transaction, Criteria criteria) {
// throw new UnsupportedOperationException("Not supported yet.");
int firstIndex = criteria.getFirstIndex();
int endIndex = criteria.getEndIndex();
List ids = new LinkedList();
int currentIndex = 0;
for (Entry entry : wikittyStorage.getWikitties().entrySet()) {
Wikitty w = entry.getValue();
String id = entry.getKey();
Restriction dto = criteria.getRestriction();
if (!w.isDeleted() && checkRestriction(transaction, dto, w)) {
currentIndex++;
if (currentIndex > firstIndex) {
ids.add(id);
}
if (endIndex >= 0 && currentIndex >= endIndex) {
break;
}
}
}
return new PagedResult(criteria.getName(),
firstIndex, ids.size(), criteria.getRestriction().toString(), null, ids);
}
@Override
public TreeNodeResult findAllChildrenCount(WikittyTransaction transaction,
String wikittyId, int depth, boolean count, Criteria filter) {
// FIXME
throw new UnsupportedOperationException("Not supported yet.");
}
private boolean checkRestriction(WikittyTransaction transaction,
Restriction restriction, Wikitty w) {
if (restriction instanceof BinaryOperator) {
BinaryOperator binOp = (BinaryOperator) restriction;
String fqfieldName = binOp.getElement().getName();
//Checks on extensions
if (Element.ELT_EXTENSION.equals(fqfieldName)) {
boolean checked = false;
switch (restriction.getName()) {
case NOT_EQUALS:
checked = !w.getExtensionNames().contains(binOp.getValue());
break;
case EQUALS:
checked = w.getExtensionNames().contains(binOp.getValue());
break;
}
return checked;
//Checks on id
} else if (Element.ELT_ID.equals(fqfieldName)) {
boolean checked = false;
switch (restriction.getName()) {
case NOT_EQUALS:
checked = !w.getWikittyId().equals(binOp.getValue());
break;
case EQUALS:
checked = w.getWikittyId().equals(binOp.getValue());
break;
}
return checked;
}
// si les wikitty n'ont meme pas l'extension concerné
// Le check restriction, ne doit pas tester les champs
// si les wikitty n'ont meme pas l'extension concerné
String[] extName = fqfieldName.split("\\.");
if (!w.hasField(extName[0], extName[1])) {
//return true in case of not equals
if (RestrictionName.NOT_EQUALS == restriction.getName()) {
return true;
}
return false;
}
// recupere la valeur dans le wikitty
Object o = w.getFqField(fqfieldName);
// recupere le type de la valeur
FieldType t = w.getFieldType(fqfieldName);
// convertie la valeur a verifier dans le meme type que la valeur
// du wikitty
Object value = binOp.getValue();
if ( !(value instanceof Collection) && t.isCollection()) {
// on doit encapsuler dans une collection, car la creation
// de la requete ajoute autant de v == o && ... que de valeurs
// dans la collection (champs multi-value solr). Mais
// dans le inmemory on doit retrouve des collections et non pas
// des objets seuls :(
value = Collections.singleton(value);
}
value = t.getValidValue(value);
boolean checked = false;
switch (restriction.getName()) {
case EQUALS:
if (value instanceof String && o instanceof String) {
String pattern = (String)value;
pattern = pattern.replace("*","\\p{ASCII}*");
pattern = pattern.replace("?","\\p{ASCII}");
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher((String)o);
checked = m.matches();
} else {
checked = value.equals(o);
}
break;
case LESS:
checked = ((Comparable) o).compareTo(value) < 0;
break;
case LESS_OR_EQUAL:
checked = ((Comparable) o).compareTo(value) <= 0;
break;
case GREATER:
checked = ((Comparable) o).compareTo(value) > 0;
break;
case GREATER_OR_EQUAL:
checked = ((Comparable) o).compareTo(value) >= 0;
break;
case NOT_EQUALS:
checked = !value.equals(o);
break;
case ENDS_WITH:
if (t.getType() != WikittyTypes.STRING) {
throw new WikittyException("Can't search for contents that 'ends with' on attribute type different of String. " + "Attribute " + fqfieldName + " is " + t.getType().name());
}
checked = ((String) o).endsWith((String) value);
break;
case STARTS_WITH:
if (t.getType() != WikittyTypes.STRING) {
throw new WikittyException("Can't search for contents that 'starts with' on attribute type different of String. " + "Attribute " + fqfieldName + " is " + t.getType().name());
}
checked = ((String) o).startsWith((String) value);
break;
}
return checked;
} else if (restriction instanceof Null) {
Null nullRes = (Null) restriction;
String fqfieldName = nullRes.getFieldName();
//check my wikitty got the right extension before doing anything.
String[] extName = fqfieldName.split("\\.");
if (!w.hasField(extName[0], extName[1])) {
return false;
}
// get the value in the wikitty
Object o = w.getFqField(fqfieldName);
//No null on extensions, always return false
if (fqfieldName.equals(Element.ELT_EXTENSION)) {
return false;
}
//No null on ids, always return false
if (fqfieldName.equals(Element.ELT_ID)) {
return false;
}
boolean checked = false;
switch (nullRes.getName()) {
case IS_NULL:
checked = (o == null);
break;
case IS_NOT_NULL:
checked = (o != null);
break;
}
return checked;
} else if (restriction instanceof In) {
In in = (In) restriction;
String fqfieldName = in.getElement().getName();
String testedValue = String.valueOf(w.getFqField(fqfieldName));
for (String value : in.getValue()){
if (testedValue.equals(value)) {
return true;
}
}
return false;
} else if (restriction instanceof True) {
return true;
} else if (restriction instanceof False) {
return false;
} else if (restriction instanceof Contains) {
Contains contains = (Contains) restriction;
String fqfieldName = contains.getElement().getName();
List values = contains.getValue();
String extension = WikittyUtil.getExtensionNameFromFQFieldName(fqfieldName);
String fieldName = WikittyUtil.getFieldNameFromFQFieldName(fqfieldName);
if (!w.hasField(extension, fieldName)) {
return false;
}
// Get field as string and then split it to take into account not
// multivalued fields.
String testedValuesAsString = w.getFieldAsString(extension, fieldName);
if('[' == testedValuesAsString.charAt(0)){
testedValuesAsString = testedValuesAsString.substring(1, testedValuesAsString.length());
}
List testedValues = Arrays.asList(testedValuesAsString.split(","));
for (Object value : values){
if (!testedValues.contains(String.valueOf(value))) {
return false;
}
}
return true;
} else if (restriction instanceof And) {
And and = (And) restriction;
for (Restriction sub : and.getRestrictions()) {
if (!checkRestriction(transaction, sub, w)) {
return false;
}
}
return true;
} else if (restriction instanceof Or) {
Or or = (Or) restriction;
for (Restriction sub : or.getRestrictions()) {
if (checkRestriction(transaction, sub, w)) {
return true;
}
}
return false;
} else if (restriction instanceof Keyword) {
Keyword keyword = (Keyword) restriction;
String value = keyword.getValue();
for(String fieldName : w.getAllFieldNames()) {
String testedValue = String.valueOf(w.getFqField(fieldName));
if (testedValue.contains(value)) {
return true;
}
}
return false;
} else if (restriction instanceof Not) {
Not or = (Not) restriction;
Restriction sub = or.getRestriction();
return !checkRestriction(transaction, sub, w);
} else if (restriction instanceof AssociatedRestriction) {
AssociatedRestriction ass = (AssociatedRestriction) restriction;
String fqfieldName = ass.getElement().getName();
//check my wikitty got the right extension before doing anything.
String[] extName = fqfieldName.split("\\.");
if (!w.hasField(extName[0], extName[1])) {
return false;
}
// get the value in the wikitty, it is a wikitty's id
Object o = w.getFqField(fqfieldName);
//Get sub-restriction
Restriction sub = ass.getRestriction();
Criteria associatedSearch = new Criteria();
associatedSearch.setRestriction(sub);
//find everything that validate the sub-restriction
PagedResult associatedResult = findAllByCriteria(transaction, associatedSearch);
List associatedList = associatedResult.getAll();
//Check that my field is contained in the sub-restriction results.
return associatedList.contains(String.valueOf(o));
} else if (restriction instanceof Between) {
Between op = (Between) restriction;
Object max = op.getMax();
Object min = op.getMin();
//No between on extensions, always return false
if (op.getElement().getName().equals(Element.ELT_EXTENSION)) {
return false;
}
//No between on ids, always return false
if (op.getElement().getName().equals(Element.ELT_ID)) {
return false;
}
String fqfieldName = op.getElement().getName();
// si les wikitty n'ont meme pas l'extension concerné
// Le check restriction, ne doit pas tester les champs
// si les wikitty n'ont meme pas l'extension concerné
String[] extName = fqfieldName.split("\\.");
if (!w.hasField(extName[0], extName[1])) {
return false;
}
// recupere la valeur dans le wikitty
Object o = w.getFqField(fqfieldName);
// recupere le type de la valeur
FieldType t = w.getFieldType(fqfieldName);
if (!(min instanceof Collection) && t.isCollection()) {
// on doit encapsuler dans une collection, car la creation
// de la requete ajoute autant de v == o && ... que de valeurs
// dans la collection (champs multi-value solr). Mais
// dans le inmemory on doit retrouve des collections et non pas
// des objets seuls :(
min = Collections.singleton(min);
}
min = t.getValidValue(min);
if (!(max instanceof Collection) && t.isCollection()) {
// on doit encapsuler dans une collection, car la creation
// de la requete ajoute autant de v == o && ... que de valeurs
// dans la collection (champs multi-value solr). Mais
// dans le inmemory on doit retrouve des collections et non pas
// des objets seuls :(
max = Collections.singleton(max);
}
max = t.getValidValue(max);
return ((Comparable) o).compareTo(min) >= 0
&& ((Comparable) o).compareTo(max) <= 0;
} else{
throw new UnsupportedOperationException(restriction.getName() + " Search Not yet implemented");
}
}
}