/*******************************************************************************
* Copyright 2006 - 2014 Vienna University of Technology,
* Department of Software Technology and Interactive Systems, IFS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package eu.scape_project.planning.manager;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.ejb.ConcurrencyManagement;
import javax.ejb.ConcurrencyManagementType;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Named;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.query.Syntax;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.util.FileManager;
import eu.scape_project.planning.model.measurement.Attribute;
import eu.scape_project.planning.model.measurement.CriterionCategory;
import eu.scape_project.planning.model.measurement.EvaluationScope;
import eu.scape_project.planning.model.measurement.Measure;
import eu.scape_project.planning.model.scales.BooleanScale;
import eu.scape_project.planning.model.scales.FloatScale;
import eu.scape_project.planning.model.scales.FreeStringScale;
import eu.scape_project.planning.model.scales.OrdinalScale;
import eu.scape_project.planning.model.scales.PositiveFloatScale;
import eu.scape_project.planning.model.scales.PositiveIntegerScale;
import eu.scape_project.planning.model.scales.RestrictedScale;
import eu.scape_project.planning.model.scales.Scale;
/**
* For administration of metrics, measurable properties and criteria. This
* should be the interface to a Measurement Property Registry (MPR) - the
* registry should be queried for all measurement entities - this would prevent
* entities being overwritten by accident, and ease notification on changed
* entities - changes to already known entities should trigger events for
* preservation watch
*
* @author kraxner
*/
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
@Startup
@Named("criteriaManager")
public class CriteriaManager implements Serializable {
private static final long serialVersionUID = -2305838596050068452L;
public static final String MEASURES_FILE = "data/vocabulary/quality_measures.rdf";
public static final String ATTRIBUTES_FILE = "data/vocabulary/quality_attributes.rdf";
public static final String CATEGORIES_FILE = "data/vocabulary/quality_categories.rdf";
private Logger log = LoggerFactory.getLogger(CriteriaManager.class);
private Model model;
/**
* Constructs a new criteria manager.
*/
public CriteriaManager() {
model = ModelFactory.createMemModelMaker().createDefaultModel();
}
/**
* Cache for looking up CriterionCategories by their URI.
*/
private Map<String, CriterionCategory> knownCategories = new HashMap<String, CriterionCategory>();
/**
* Cache for lookup of all currently known measures by their URI.
*
*/
private Map<String, Measure> knownMeasures = new HashMap<String, Measure>();
/**
* Cache for lookup of all currently known attributes by their URI.
*/
private Map<String, Attribute> knownAttributes = new HashMap<String, Attribute>();
/**
* Returns a list of all known categories IMPORTANT: this list can not (and
* must not) be altered!
*
* @return a collection of categories
*/
public Collection<CriterionCategory> getAllCriterionCategories() {
return Collections.unmodifiableCollection(knownCategories.values());
}
/**
* Returns a list of all known criteria IMPORTANT: this list can not (and
* must not) be altered!
*
* @return a collection of measures
*/
@Lock(LockType.READ)
public Collection<Measure> getAllMeasures() {
return Collections.unmodifiableCollection(knownMeasures.values());
}
/**
* Returns a list of all known properties IMPORTANT: this list can not (and
* must not) be altered!
*
* @return a collection of attributes
*/
@Lock(LockType.READ)
public Collection<Attribute> getAllAttributes() {
return Collections.unmodifiableCollection(knownAttributes.values());
}
/**
* Returns the criterion for the given criterionUri.
*
* @param measureUri
* the URI of the measure
* @return the measure or null if no measure could be found
*/
@Lock(LockType.READ)
public Measure getMeasure(String measureUri) {
return knownMeasures.get(measureUri);
}
/**
* Returns the attribute for the given attributeUri.
*
* @param attributeUri
* the URI of the attribute
* @return the attribute of null if no attribute could be found
*/
@Lock(LockType.READ)
public Attribute getAttribute(String attributeUri) {
return knownAttributes.get(attributeUri);
}
/**
* Builds the hierarchy of the measure.
*
* @param measureUri
* the URI of the measure
* @return a list of URIs ordered from measure upwards
*/
@Lock(LockType.READ)
public List<String> getCategoryHierachy(String measureUri) {
List<String> hierarchy = new ArrayList<String>();
Measure m = knownMeasures.get(measureUri);
if (m == null) {
return hierarchy;
}
Attribute a = knownAttributes.get(m.getAttribute().getUri());
if (a == null) {
return hierarchy;
}
hierarchy.add(0, a.getName());
CriterionCategory criterionCategory = a.getCategory();
if (criterionCategory == null) {
return hierarchy;
}
hierarchy.add(0, criterionCategory.getName());
return hierarchy;
}
/**
* measureUri categories from the model.
*/
private void resolveCriterionCategories() {
knownCategories.clear();
// @formatter:off
String statement = "SELECT ?c ?cn ?scope WHERE { "
+ "?c rdf:type quality:CriterionCategory . "
+ "?c skos:prefLabel ?cn . " + "?c quality:scope ?scope }";
String commonNS = "PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#> "
+ "PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#> "
+ "PREFIX skos:<http://www.w3.org/2004/02/skos/core#> "
+ "PREFIX quality: <http://purl.org/DP/quality#>";
// @formatter:on
Query query = QueryFactory.create(commonNS + statement, Syntax.syntaxARQ);
QueryExecution qe = QueryExecutionFactory.create(query, model);
ResultSet results = qe.execSelect();
while ((results != null) && (results.hasNext())) {
QuerySolution qs = results.next();
String categoryId = qs.getResource("c").toString();
String name = qs.getLiteral("cn").getString();
String scopeStr = qs.getResource("scope").getLocalName();
EvaluationScope scope = null;
if ("OBJECT".equals(scopeStr)) {
scope = EvaluationScope.OBJECT;
} else {
scope = EvaluationScope.ALTERNATIVE_ACTION;
}
if (scope != null) {
CriterionCategory category = new CriterionCategory(categoryId, name, scope);
knownCategories.put(categoryId, category);
} else {
log.warn("CriterionCategory without defined scope: " + categoryId + ", " + name);
}
}
}
/**
* Resolves attributes from the model.
*/
private void resolveAttributes() {
knownAttributes.clear();
// @formatter:off
String statement = "SELECT ?a ?an ?ad ?ac WHERE { "
+ "?a rdf:type quality:Attribute . "
+ "?a skos:prefLabel ?an . "
+ "?a dct:description ?ad . "
+ "?a quality:criterionCategory ?ac }";
String commonNS = "PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#> "
+ "PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#> "
+ "PREFIX dct:<http://purl.org/dc/terms/> "
+ "PREFIX skos:<http://www.w3.org/2004/02/skos/core#> "
+ "PREFIX quality: <http://purl.org/DP/quality#>";
// @formatter:on
Query query = QueryFactory.create(commonNS + statement, Syntax.syntaxARQ);
QueryExecution qe = QueryExecutionFactory.create(query, model);
ResultSet results = qe.execSelect();
while ((results != null) && (results.hasNext())) {
QuerySolution qs = results.next();
Attribute a = new Attribute();
a.setDescription(qs.getLiteral("ad").getString());
a.setName(qs.getLiteral("an").getString());
a.setUri(qs.getResource("a").toString());
String categoryUri = qs.getResource("ac").toString();
a.setCategory(knownCategories.get(categoryUri));
knownAttributes.put(a.getUri(), a);
}
}
/**
* Resolves measures from the model.
*/
private void resolveMeasures() {
knownMeasures.clear();
// @formatter:off
String statement = "SELECT ?m ?mn ?md ?a ?s ?r WHERE { "
+ "?m rdf:type quality:Measure . "
+ "?m quality:attribute ?a . "
+ "?m skos:prefLabel ?mn . "
+ "?m dct:description ?md . "
+ "?m quality:scale ?s . "
+ "optional{?m quality:restriction ?r} }";
String commonNS = "PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#> "
+ "PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#> "
+ "PREFIX dct:<http://purl.org/dc/terms/> "
+ "PREFIX skos:<http://www.w3.org/2004/02/skos/core#> "
+ "PREFIX quality: <http://purl.org/DP/quality#>";
// @formatter:on
Query query = QueryFactory.create(commonNS + statement, Syntax.syntaxARQ);
QueryExecution qe = QueryExecutionFactory.create(query, model);
ResultSet results = qe.execSelect();
while ((results != null) && (results.hasNext())) {
QuerySolution qs = results.next();
Resource attribute = qs.getResource("a");
String attributeUri = attribute.toString();
Measure m = new Measure();
m.setUri(qs.getResource("m").toString());
m.setName(qs.getLiteral("mn").getString());
m.setDescription(qs.getLiteral("md").getString());
Scale s = createScale(qs.getResource("s").getLocalName());
m.setScale(s);
if ((s instanceof RestrictedScale) && (qs.contains("r"))) {
String restriction = qs.getLiteral("r").getString();
((RestrictedScale) s).setRestriction(restriction);
}
Attribute a = knownAttributes.get(attributeUri);
m.setAttribute(a);
if ((a != null) && (s != null)) {
// only add completely defined measures
knownMeasures.put(m.getUri(), m);
}
}
}
/**
* Creates a scale from the provided scale name.
*
* @param scaleName
* the name of the scale
* @return the scale or null if no scale could be found
*/
private Scale createScale(String scaleName) {
if ("Boolean".equalsIgnoreCase(scaleName)) {
return new BooleanScale();
} else if ("FreeText".equalsIgnoreCase(scaleName)) {
return new FreeStringScale();
} else if ("Number".equalsIgnoreCase(scaleName)) {
return new FloatScale();
} else if ("PositiveNumber".equalsIgnoreCase(scaleName)) {
return new PositiveFloatScale();
} else if ("PositiveInteger".equalsIgnoreCase(scaleName)) {
return new PositiveIntegerScale();
} else if ("Ordinal".equalsIgnoreCase(scaleName)) {
return new OrdinalScale();
}
return null;
}
/**
* FIXME: reload from RDF
*
* Reads the XML file from {@link #DESCRIPTOR_FILE} and adds the contained
* criteria to the database. For criteria that already exist in the database
* (as designated by URI), the information is updated.
*
* @see eu.scape_project.planning.application.ICriteriaManager#reload()
* ATTENTION: From all available CRUD operation only Create and Update
* are covered. Delete operations are not executed. Thus, if you have
* deleted Properties in your XML they are not deleted from the
* database.
*/
@Lock(LockType.WRITE)
public void reload() {
model = FileManager.get().loadModel(CATEGORIES_FILE);
model.add(FileManager.get().loadModel(ATTRIBUTES_FILE));
model.add(FileManager.get().loadModel(MEASURES_FILE));
resolveCriterionCategories();
resolveAttributes();
resolveMeasures();
}
/**
* Initialises this criteria manager.
*/
@PostConstruct
public void init() {
if (knownMeasures.isEmpty()) {
reload();
}
}
}