eu.morfeoproject.fast.catalogue.Catalogue.java Source code

Java tutorial

Introduction

Here is the source code for eu.morfeoproject.fast.catalogue.Catalogue.java

Source

/**
 * Copyright (c) 2008-2011, FAST Consortium
 * 
 * This file is part of FAST Platform.
 * 
 * FAST Platform is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * FAST Platform 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 Affero General Public
 * License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with FAST Platform. If not, see <http://www.gnu.org/licenses/>.
 * 
 * Info about members and contributors of the FAST Consortium
 * is available at http://fast.morfeo-project.eu
 *
 **/
package eu.morfeoproject.fast.catalogue;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.UUID;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.commontag.AuthorCTag;
import org.commontag.AutoCTag;
import org.commontag.CTag;
import org.commontag.ReaderCTag;
import org.ontoware.aifbcommons.collection.ClosableIterator;
import org.ontoware.rdf2go.RDF2Go;
import org.ontoware.rdf2go.exception.ModelRuntimeException;
import org.ontoware.rdf2go.model.Model;
import org.ontoware.rdf2go.model.QueryResultTable;
import org.ontoware.rdf2go.model.QueryRow;
import org.ontoware.rdf2go.model.Statement;
import org.ontoware.rdf2go.model.Syntax;
import org.ontoware.rdf2go.model.node.BlankNode;
import org.ontoware.rdf2go.model.node.DatatypeLiteral;
import org.ontoware.rdf2go.model.node.LanguageTagLiteral;
import org.ontoware.rdf2go.model.node.Node;
import org.ontoware.rdf2go.model.node.Resource;
import org.ontoware.rdf2go.model.node.URI;
import org.ontoware.rdf2go.model.node.UriOrVariable;
import org.ontoware.rdf2go.model.node.Variable;
import org.ontoware.rdf2go.vocabulary.OWL;
import org.ontoware.rdf2go.vocabulary.RDF;
import org.ontoware.rdf2go.vocabulary.RDFS;
import org.ontoware.rdf2go.vocabulary.XSD;
import org.openrdf.repository.RepositoryException;
import org.openrdf.rio.RDFParseException;

import uk.ac.open.kmi.iserve.IServeClient;
import uk.ac.open.kmi.iserve.IServeConfiguration;
import uk.ac.open.kmi.iserve.IServeResponse;
import eu.morfeoproject.fast.catalogue.builder.BuildingBlockRDF2GoBuilder;
import eu.morfeoproject.fast.catalogue.builder.SampleRDF2GoBuilder;
import eu.morfeoproject.fast.catalogue.model.Action;
import eu.morfeoproject.fast.catalogue.model.BackendService;
import eu.morfeoproject.fast.catalogue.model.BuildingBlock;
import eu.morfeoproject.fast.catalogue.model.Concept;
import eu.morfeoproject.fast.catalogue.model.Condition;
import eu.morfeoproject.fast.catalogue.model.Form;
import eu.morfeoproject.fast.catalogue.model.Operator;
import eu.morfeoproject.fast.catalogue.model.Pipe;
import eu.morfeoproject.fast.catalogue.model.Property;
import eu.morfeoproject.fast.catalogue.model.Sample;
import eu.morfeoproject.fast.catalogue.model.Screen;
import eu.morfeoproject.fast.catalogue.model.ScreenComponent;
import eu.morfeoproject.fast.catalogue.model.ScreenFlow;
import eu.morfeoproject.fast.catalogue.model.Trigger;
import eu.morfeoproject.fast.catalogue.model.factory.BuildingBlockFactory;
import eu.morfeoproject.fast.catalogue.ontologies.DefaultOntologies;
import eu.morfeoproject.fast.catalogue.ontologies.OntologyFetcher;
import eu.morfeoproject.fast.catalogue.ontologies.OntologyFinder;
import eu.morfeoproject.fast.catalogue.ontologies.SindiceOntologyFinder;
import eu.morfeoproject.fast.catalogue.ontologies.DefaultOntologies.Ontology;
import eu.morfeoproject.fast.catalogue.ontologies.DefaultOntologies.PublicOntology;
import eu.morfeoproject.fast.catalogue.planner.Plan;
import eu.morfeoproject.fast.catalogue.planner.PlannerFactory;
import eu.morfeoproject.fast.catalogue.planner.ScreenPlanner;
import eu.morfeoproject.fast.catalogue.recommender.Recommender;
import eu.morfeoproject.fast.catalogue.recommender.ScreenComponentRecommender;
import eu.morfeoproject.fast.catalogue.recommender.ScreenRecommender;
import eu.morfeoproject.fast.catalogue.util.DateFormatter;
import eu.morfeoproject.fast.catalogue.util.MiscUtil;
import eu.morfeoproject.fast.catalogue.vocabulary.CTAG;
import eu.morfeoproject.fast.catalogue.vocabulary.DC;
import eu.morfeoproject.fast.catalogue.vocabulary.FGO;
import eu.morfeoproject.fast.catalogue.vocabulary.OMV;

/**
 * Catalogue
 * 
 * @author irivera
 */
public class Catalogue {

    protected final Log log = LogFactory.getLog(this.getClass());

    private CatalogueConfiguration configuration;
    private TripleStore tripleStore;
    private ScreenPlanner screenPlanner;
    private Recommender screenRecommender;
    private Recommender scRecommender;
    private String environment;
    private URI prototypesGraph;
    private URI clonesGraph;
    private URI ontologiesMetadataGraph;

    public Catalogue(CatalogueConfiguration conf) {
        this(conf, "default");
    }

    public Catalogue(CatalogueConfiguration conf, String environment) {
        this.configuration = conf;
        this.environment = environment;

        try {
            init();
        } catch (Exception e) {
            throw new RuntimeException("Unable to start the catalogue service: " + e, e);
        }
    }

    protected void init() throws ConfigurationException, TripleStoreException {
        // read configuration
        String serverURL = this.configuration.get(this.environment, "serverURL");
        String sesameServer = this.configuration.get(this.environment, "sesameServer");
        String repositoryID = this.configuration.get(this.environment, "repositoryID");
        String storageDir = this.configuration.get(this.environment, "storageDir");
        String indexes = this.configuration.get(this.environment, "indexes");

        // connect to local or remote triple store
        tripleStore = (sesameServer != null && repositoryID != null)
                ? new RemoteTripleStore(sesameServer, repositoryID)
                : new LocalTripleStore(storageDir + "/triplestore", indexes);
        tripleStore.open();

        // creates the URIs for the prototypes/clones graphs
        prototypesGraph = tripleStore.createURI(serverURL + "/prototypes");
        clonesGraph = tripleStore.createURI(serverURL + "/clones");

        // creates a graph for keeping metadata about the ontologies
        ontologiesMetadataGraph = tripleStore.createURI(serverURL + "/ontologies/metadata");

        // check if the catalogue is correct
        if (!check()) {
            // recover the catalogue
            restore();
        }

        // creates the screen planner
        screenPlanner = PlannerFactory.createScreenPlanner(this);

        // creates the recommenders
        screenRecommender = new ScreenRecommender(this, storageDir + "/recommender");
        scRecommender = new ScreenComponentRecommender(this, storageDir + "/recommender");
    }

    public URL getServerURL() {
        return configuration.getURL(this.environment, "serverURL");
    }

    public String getStoragePath() {
        return this.configuration.get(this.environment, "storageDir");
    }

    /**
     * Restores the catalogue Should only be done when {@link #check()} returns
     * true.
     */
    protected void restore() {
        // add default ontologies
        for (DefaultOntologies.Ontology ontology : DefaultOntologies.getDefaults()) {
            if (log.isInfoEnabled())
                log.info("adding default ontology '" + ontology.getUri() + "'");
            addOntology(ontology);
        }
    }

    protected boolean addOntology(Ontology ontology) {
        URI uri = ontology.getUri();
        try {
            boolean added = tripleStore.addOntology(uri, ontology.getInputStream(), ontology.getSyntax());
            if (added) {
                tripleStore.addStatement(ontologiesMetadataGraph, uri, RDF.type, OMV.Ontology);
                tripleStore.addStatement(ontologiesMetadataGraph, uri, OMV.isLocatedAt, uri);
                tripleStore.addStatement(ontologiesMetadataGraph, uri, OMV.resourceLocator,
                        tripleStore.createURI(ontology.getLocation().toString()));
                tripleStore.addStatement(ontologiesMetadataGraph, uri, OMV.hasSyntax,
                        tripleStore.createDatatypeLiteral(ontology.getSyntax().toString(), XSD._string));
            }
        } catch (OntologyInvalidException e) {
            log.error("Cannot add ontology '" + uri + "': " + e, e);
        } catch (RepositoryException e) {
            log.error("Cannot add ontology '" + uri + "': " + e, e);
        } catch (RDFParseException e) {
            log.error("Cannot add ontology '" + uri + "': " + e, e);
        } catch (IOException e) {
            log.error("Cannot read ontology '" + uri + "': " + e, e);
        }
        return false;
    }

    public boolean addPublicOntology(URI uri, String downloadUri, Syntax syntax) {
        return addOntology(new PublicOntology(uri, downloadUri, syntax));
    }

    public boolean containsOntology(URI ontUri) {
        ClosableIterator<Statement> cIt = tripleStore.findStatements(ontologiesMetadataGraph, ontUri, RDF.type,
                OMV.Ontology);
        boolean contains = cIt.hasNext();
        cIt.close();
        return contains;
    }

    public List<URI> getAllOntologies() {
        LinkedList<URI> ontologies = new LinkedList<URI>();
        ClosableIterator<Statement> cIt = tripleStore.findStatements(ontologiesMetadataGraph, Variable.ANY,
                RDF.type, OMV.Ontology);
        for (; cIt.hasNext();) {
            URI uri = cIt.next().getSubject().asURI();
            if (!ontologies.contains(uri))
                ontologies.add(uri);
        }
        cIt.close();
        return ontologies;
    }

    public void clear() {
        // clear the repository
        tripleStore.clear();
        // clear the screen planner
        screenPlanner.clear();
        // clear recommenders
        screenRecommender.rebuild();
        scRecommender.rebuild();
        // restores default ontologies
        restore();
    }

    public boolean check() {
        return checkDefaultOntologies();
    }

    /**
     * This checks if the default ontologies are there or not. This can only
     * work after init() is called.
     * 
     * @return false, if {@link #restore()} needs to be called.
     */
    public boolean checkDefaultOntologies() {
        boolean result = false;
        // are the default ontologies here?
        for (DefaultOntologies.Ontology ont : DefaultOntologies.getDefaults()) {
            if (log.isInfoEnabled())
                log.info("checking ontology '" + ont.getUri() + "'...");
            boolean misses = !containsOntology(ont.getUri());
            if (log.isInfoEnabled())
                log.info("default ontology '" + ont.getUri() + "' is in the store: " + !misses);
            result = result || misses;
        }
        return !result;
    }

    public void open() throws RepositoryException {
        tripleStore.open();
    }

    public void close() {
        tripleStore.close();
    }

    public boolean containsBuildingBlock(URI uri) {
        return tripleStore.isResource(uri, FGO.ScreenFlow) || tripleStore.isResource(uri, FGO.Screen)
                || tripleStore.isResource(uri, FGO.Form) || tripleStore.isResource(uri, FGO.Operator)
                || tripleStore.isResource(uri, FGO.BackendService);
    }

    public boolean containsBuildingBlock(BuildingBlock bb) {
        return containsBuildingBlock(bb.getUri());
    }

    public boolean containsResource(URI uri) {
        return tripleStore.isResource(uri);
    }

    public boolean containsConcept(URI uri) {
        return isConcept(uri);
    }

    /**
     * 
     * @param canvasList list of building blocks (clones) in a certain canvas. the results
     * will try satisfy this list, unless 'selectedItem' has a value
     * @param selectedItem the results will be optimised for this element, as if the canvas
     * has only one element (results won't contain repeated building blocks from the canvas)
     * @param conditions list of conditions to be satisfied
     * @param offset
     * @param limit
     * @param tags
     * @param strategy
     * @return a list of suggested building block URIs
     * @throws ClassCastException
     * @throws ModelRuntimeException
     */
    public List<URI> findScreenComponents(List<ScreenComponent> canvasList, ScreenComponent selectedItem,
            List<Condition> conditions, int offset, int limit, List<String> tags, int strategy)
            throws ClassCastException, ModelRuntimeException {

        if (limit == 0)
            return new ArrayList<URI>();

        boolean prepost = Constants.PREPOST == (strategy & Constants.PREPOST);
        boolean patterns = Constants.PATTERNS == (strategy & Constants.PATTERNS);

        ArrayList<URI> queryList = new ArrayList<URI>();
        List<URI> suggestionList = new ArrayList<URI>();

        //---------------------------//
        // queries the repository searching for building blocks compatible in terms of their pre/postconditions 
        //---------------------------//

        if (prepost) {
            String queryString = "SELECT DISTINCT ?bb WHERE {" + " { GRAPH " + prototypesGraph.toSPARQL() + " {"
                    + " { ?bb " + RDF.type.toSPARQL() + " " + FGO.Form.toSPARQL() + " } UNION" + " { ?bb "
                    + RDF.type.toSPARQL() + " " + FGO.Operator.toSPARQL() + " } UNION" + " { ?bb "
                    + RDF.type.toSPARQL() + " " + FGO.BackendService.toSPARQL() + " } } .";

            // doesn't include the building blocks where the postconditions were taken from
            for (ScreenComponent sc : canvasList) {
                queryString = queryString.concat("FILTER (?bb != " + sc.getPrototype().toSPARQL() + ") . ");
            }

            // tags
            if (tags != null && tags.size() > 0) {
                queryString = queryString.concat("{");
                for (String tag : tags) {
                    queryString = queryString.concat(
                            " { ?bb " + CTAG.tagged.toSPARQL() + " ?ctag . " + " ?ctag " + CTAG.label.toSPARQL()
                                    + " ?tag . " + " FILTER(regex(str(?tag), \"" + tag + "\", \"i\")) } UNION");
                }
                // remove last 'UNION'
                if (queryString.endsWith("UNION"))
                    queryString = queryString.substring(0, queryString.length() - 5);
                queryString = queryString.concat("} . ");
            }

            // search for components which preconditions (from actions) are
            // satisfied by any of the conditions given
            if (conditions.size() > 0) {
                queryString = queryString.concat("{");
                for (Condition con : conditions) {
                    ClosableIterator<Statement> cIt = null;
                    // query for building blocks with the condition as an action's precondition
                    queryString = queryString.concat(" { ?bb " + FGO.hasAction.toSPARQL() + " ?a . ");
                    queryString = queryString.concat(" ?a " + FGO.hasPreCondition.toSPARQL() + " ?c . ");
                    queryString = queryString.concat(" ?c " + FGO.hasPattern.toSPARQL() + " ?p . ");
                    queryString = queryString.concat("GRAPH ?p {");
                    cIt = patternToRDF2GoModel(con.getPatternString()).iterator();
                    for (; cIt.hasNext();) {
                        Statement st = cIt.next();
                        Resource subject = st.getSubject();
                        Node object = st.getObject();
                        String s = (subject instanceof BlankNode) ? toCleanVariable(subject.toString())
                                : subject.toSPARQL();
                        String o = (object instanceof BlankNode) ? toCleanVariable(object.toString())
                                : object.toSPARQL();
                        queryString = queryString.concat(s + " " + st.getPredicate().toSPARQL() + " " + o + " . ");
                    }
                    cIt.close();
                    queryString = queryString.concat("} } UNION");
                    // query for building blocks with the condition as a postcondition
                    queryString = queryString.concat(" { ?bb " + FGO.hasPostCondition.toSPARQL() + " ?c . ");
                    queryString = queryString.concat(" ?c " + FGO.hasPattern.toSPARQL() + " ?p . ");
                    queryString = queryString.concat("GRAPH ?p {");
                    cIt = patternToRDF2GoModel(con.getPatternString()).iterator();
                    for (; cIt.hasNext();) {
                        Statement st = cIt.next();
                        Resource subject = st.getSubject();
                        Node object = st.getObject();
                        String s = (subject instanceof BlankNode) ? toCleanVariable(subject.toString())
                                : subject.toSPARQL();
                        String o = (object instanceof BlankNode) ? toCleanVariable(object.toString())
                                : object.toSPARQL();
                        queryString = queryString.concat(s + " " + st.getPredicate().toSPARQL() + " " + o + " . ");
                    }
                    cIt.close();
                    queryString = queryString.concat("} } UNION");
                }
                queryString = queryString.substring(0, queryString.length() - 5); // remove last 'UNION'
                queryString = queryString.concat("}");
            }
            queryString = queryString.concat("} }");

            if (log.isInfoEnabled())
                log.info(queryString);
            QueryResultTable qrt = tripleStore.sparqlSelect(queryString);
            ClosableIterator<QueryRow> itResults = qrt.iterator();
            while (itResults.hasNext()) {
                queryList.add(itResults.next().getValue("bb").asURI());
            }
            itResults.close();
        }

        //---------------------------//
        // ask the recommender for building blocks related to the current canvas
        //---------------------------//

        if (patterns) {
            ArrayList<URI> uriList = new ArrayList<URI>();
            if (selectedItem == null) {
                for (ScreenComponent sc : canvasList) {
                    URI uri = getPrototypeOfClone(sc.getUri());
                    if (uri != null && !uriList.contains(uri))
                        uriList.add(uri);
                }
            } else {
                uriList.add(selectedItem.getUri());
            }
            suggestionList.addAll(scRecommender.getSuggestionList(uriList));
            if (log.isInfoEnabled()) {
                log.info("--- Recommendation algorithm ---");
                log.info("Input: " + uriList);
                log.info("Ouput: " + suggestionList);
            }
        }

        //---------------------------//
        // merge the results of both building blocks lists
        //---------------------------//

        List<URI> results = new ArrayList<URI>();
        results.addAll(suggestionList); // most commonly used together
        for (URI uri : queryList) { // adds compatible building blocks
            if (!results.contains(uri)) {
                results.add(uri);
            }
        }

        // do pagination
        int count = results.size();
        if (results.size() > 0 && (offset > 0 || limit > -1)) {
            int toIndex = limit > -1 ? Math.min(offset + limit, count) : count;
            results = results.subList(offset, toIndex);
        }

        return results;
    }

    /**
     * Search for screens inside the catalogue which satisfy the unsatisfied
     * preconditions of a list of screens and also takes into account the domain
     * context composed by a list of tags. If within the list of screens there
     * are not unsatisfied preconditions, no screens will be returned because it
     * is assume that there is no need to satisfied any condition. If any tag is
     * given, the list of screens to be returned will be filtered by this tag.
     * If no tag is given, no tag filter will be done. If the number of screens
     * returned is high, you can use the parameters OFFSET and LIMIT in order to
     * retrieve small lists of screens.
     * 
     * @param screens
     *            list of screens (must be a list of 'clones')
     * @param plugin
     *            ignored at this version
     * @param subsume
     *            ignored at this version
     * @param offset
     *            indicate the start of the list of screens to be returned
     * @param limit
     *            specify the maximum number of screens to be returned [negative
     *            means no limit]
     * @param tags
     *            a list of tags for filtering the results
     * @return a recommended list of screens according to the input given
     * @throws ClassCastException
     * @throws ModelRuntimeException
     */
    protected List<URI> findScreens(List<Screen> screens, List<Condition> preconditions,
            List<Condition> postconditions, boolean plugin, boolean subsume, int offset, int limit,
            List<String> tags, URI predicate) throws ModelRuntimeException {

        if (limit == 0)
            return new ArrayList<URI>();

        ArrayList<URI> results = new ArrayList<URI>();

        String queryString = "SELECT DISTINCT ?bb" + " WHERE { { GRAPH " + prototypesGraph.toSPARQL() + " { ?bb "
                + RDF.type.toSPARQL() + " " + FGO.Screen.toSPARQL() + " } . ";

        for (Screen screen : screens) {
            //FIXME should we accept prototypes and clones in this method?
            URI filterURI = screen.getPrototype() == null ? screen.getUri() : screen.getPrototype();
            queryString = queryString.concat("FILTER (?bb != " + filterURI.toSPARQL() + ") . ");
        }

        if (tags != null && tags.size() > 0) {
            queryString = queryString.concat("{");
            for (String tag : tags) {
                queryString = queryString
                        .concat(" { ?bb " + CTAG.tagged.toSPARQL() + " ?ctag . " + " ?ctag " + CTAG.label.toSPARQL()
                                + " ?tag . " + " FILTER(regex(str(?tag), \"" + tag + "\", \"i\")) } UNION");
            }
            // remove last 'UNION'
            if (queryString.endsWith("UNION"))
                queryString = queryString.substring(0, queryString.length() - 5);
            queryString = queryString.concat("} . ");
        }

        ArrayList<Condition> unCon = getUnsatisfiedConditions(screens, preconditions, postconditions, plugin,
                subsume);
        if (unCon.size() > 0) {
            queryString = queryString.concat("{");
            for (Condition con : unCon) {
                if (log.isDebugEnabled())
                    log.debug("[UNSATISFIED] " + con.toString());
                queryString = queryString.concat("{ ?bb " + predicate.toSPARQL() + " ?condition . ");
                queryString = queryString.concat(" ?condition " + FGO.hasPattern.toSPARQL() + " ?pattern . ");
                queryString = queryString.concat("GRAPH ?pattern {");
                ClosableIterator<Statement> it = patternToRDF2GoModel(con.getPatternString()).iterator();
                for (; it.hasNext();) {
                    Statement st = it.next();
                    Resource subject = st.getSubject();
                    Node object = st.getObject();
                    String s = (subject instanceof BlankNode) ? toCleanVariable(subject.toString())
                            : subject.toSPARQL();
                    String o = (object instanceof BlankNode) ? toCleanVariable(object.toString())
                            : object.toSPARQL();
                    queryString = queryString.concat(s + " " + st.getPredicate().toSPARQL() + " " + o + " . ");
                }
                it.close();
                queryString = queryString.concat("} } UNION");
            }
            // remove last 'UNION'
            if (queryString.endsWith("UNION"))
                queryString = queryString.substring(0, queryString.length() - 5);
            queryString = queryString.concat("}");
        }
        queryString = queryString.concat("} }");

        if (limit > 0)
            queryString = queryString.concat(" LIMIT " + limit);
        queryString = queryString.concat(" OFFSET " + offset);

        if (log.isInfoEnabled())
            log.info(queryString);
        QueryResultTable qrt = tripleStore.sparqlSelect(queryString);
        ClosableIterator<QueryRow> itResults = qrt.iterator();
        while (itResults.hasNext()) {
            results.add(itResults.next().getValue("bb").asURI());
        }
        itResults.close();

        return results;
    }

    public List<URI> findScreensBackwards(List<Screen> screens, List<Condition> preconditions,
            List<Condition> postconditions, boolean plugin, boolean subsume, int offset, int limit,
            List<String> tags) throws ModelRuntimeException {
        return findScreens(screens, preconditions, postconditions, plugin, subsume, offset, limit, tags,
                FGO.hasPostCondition);
    }

    public List<URI> findScreensForwards(List<Screen> screens, List<Condition> preconditions,
            List<Condition> postconditions, boolean plugin, boolean subsume, int offset, int limit,
            List<String> tags) throws ModelRuntimeException {
        ArrayList<Screen> tmpScreenList = new ArrayList<Screen>();
        for (Screen screen : screens) {
            Screen s = BuildingBlockFactory.createScreen();
            s.setUri(screen.getUri());
            s.setPreconditions(screen.getPostconditions());
            tmpScreenList.add(s);
        }
        return findScreens(tmpScreenList, postconditions, preconditions, plugin, subsume, offset, limit, tags,
                FGO.hasPreCondition);
    }

    public List<URI> findScreens(List<Screen> screens, Screen selectedItem, List<Condition> preconditions,
            List<Condition> postconditions, boolean plugin, boolean subsume, int offset, int limit,
            List<String> tags, int strategy) {

        if (limit == 0)
            return new ArrayList<URI>();

        boolean prepost = Constants.PREPOST == (strategy & Constants.PREPOST);
        boolean patterns = Constants.PATTERNS == (strategy & Constants.PATTERNS);

        ArrayList<URI> queryList = new ArrayList<URI>();
        List<URI> suggestionList = new ArrayList<URI>();

        //---------------------------//
        // queries the repository searching for building blocks compatible in terms of their pre/postconditions 
        //---------------------------//

        if (prepost) {
            queryList.addAll(findScreensBackwards(screens, preconditions, postconditions, plugin, subsume, offset,
                    limit, tags));
            if (!screens.isEmpty())
                queryList.addAll(findScreensForwards(screens, new ArrayList<Condition>(),
                        new ArrayList<Condition>(), plugin, subsume, offset, limit, tags));
        }

        //---------------------------//
        // ask the recommender for building blocks related to the current canvas
        //---------------------------//

        if (patterns) {
            ArrayList<URI> uriList = new ArrayList<URI>();
            if (selectedItem == null) {
                for (Screen screen : screens) {
                    URI uri = getPrototypeOfClone(screen.getUri());
                    if (uri != null && !uriList.contains(uri))
                        uriList.add(uri);
                }
            } else {
                uriList.add(selectedItem.getUri());
            }
            suggestionList.addAll(screenRecommender.getSuggestionList(uriList));
            if (log.isInfoEnabled()) {
                log.info("--- Recommendation algorithm ---");
                log.info("Input: " + uriList);
                log.info("Ouput: " + suggestionList);
            }
        }

        //---------------------------//
        // merge the results of both building blocks lists
        //---------------------------//

        List<URI> results = new ArrayList<URI>();
        results.addAll(suggestionList); // most commonly used together
        for (URI uri : queryList) { // adds compatible building blocks
            if (!results.contains(uri)) {
                results.add(uri);
            }
        }

        // do pagination
        int count = results.size();
        if (results.size() > 0 && (offset > 0 || limit > -1)) {
            int toIndex = limit > -1 ? Math.min(offset + limit, count) : count;
            results = results.subList(offset, toIndex);
        }

        return results;
    }

    /**
     * Retrieves all the unsatisfied preconditions of a list of screens. It also
     * checks whether a 'postcondition' is satisfied. NOTE: 'preconditions' are
     * always satisfied
     * 
     * @param screenList
     *            A list of screens (to extract their pre/postconditions)
     * @param preconditions
     *            A list of preconditions
     * @param postconditions
     *            A list of postconditions
     * @param plugin
     *            ignored at this version
     * @param subsume
     *            ignored at this version
     * @return a list of conditions which are unsatisfied
     */
    protected ArrayList<Condition> getUnsatisfiedConditions(List<Screen> screenList, List<Condition> preconditions,
            List<Condition> postconditions, boolean plugin, boolean subsume) {
        ArrayList<Condition> unsatisfied = new ArrayList<Condition>();
        for (Screen screen : screenList) {
            for (Condition condition : screen.getPreconditions()) {
                if (condition.isPositive() && !isConditionSatisfied(preconditions, screenList, condition, plugin,
                        subsume, screen.getUri())) {
                    unsatisfied.add(condition);
                }
            }
        }
        for (Condition condition : postconditions) {
            if (condition.isPositive()
                    && !isConditionSatisfied(preconditions, screenList, condition, plugin, subsume, null)) {
                unsatisfied.add(condition);
            }
        }
        return unsatisfied;
    }

    /**
     * Determines if a specific precondition is satisfied by a list of screens,
     * in other words, check if any postcondition of the list of screens satisfy
     * the precondition. Usually the list of screens will include the screen
     * which the precondition belongs to, hence you can set if a screen has to
     * be excluded while checking.
     * 
     * @param screenList
     *            a list of screens which might satisfy the precondition
     * @param conditionToCheck
     *            the condition to check if it is satisfied
     * @param plugin
     *            not yet implemented
     * @param subsume
     *            not yet implemented
     * @param screenExcluded
     *            the URI of the screen which will be ignored while checking
     * @return true if the condition is satisfied
     */
    public boolean isConditionSatisfied(List<Condition> conditionList, List<Screen> screenList,
            Condition conditionToCheck, boolean plugin, boolean subsume, URI screenExcluded) {
        ArrayList<Condition> listToCheck = new ArrayList<Condition>(1);
        listToCheck.add(conditionToCheck);
        return isConditionListSatisfied(screenList, conditionList, listToCheck, plugin, subsume, screenExcluded);
    }

    public boolean isConditionListSatisfied(List<Screen> screenList, List<Condition> conditionList,
            List<Condition> conditionsToCheck, boolean plugin, boolean subsume, URI screenExcluded) {
        // if no conditions are provided, then returns true
        if (conditionsToCheck.isEmpty())
            return true;

        // creates the model of the already satisfied conditions
        Model model = RDF2Go.getModelFactory().createModel();
        model.open();
        for (Condition condition : conditionList) {
            if (condition.isPositive()) {
                model.addModel(patternToRDF2GoModel(condition.getPatternString()));
            }
        }
        for (Screen screen : screenList) {
            if (!screen.getUri().equals(screenExcluded)) {
                for (Condition condition : screen.getPostconditions()) {
                    if (condition.isPositive()) {
                        model.addModel(patternToRDF2GoModel(condition.getPatternString()));
                    }
                }
            }
        }

        // create the ASK sparql query for a precondition
        String queryStr = "ASK {";
        for (Condition condition : conditionsToCheck) {
            if (condition.isPositive()) {
                ClosableIterator<Statement> it = patternToRDF2GoModel(condition.getPatternString()).iterator();
                for (; it.hasNext();) {
                    Statement st = it.next();
                    String subject = (st.getSubject() instanceof BlankNode)
                            ? toCleanVariable(st.getSubject().toString())
                            : st.getSubject().toSPARQL();
                    String object = (st.getObject() instanceof BlankNode)
                            ? toCleanVariable(st.getObject().toString())
                            : st.getObject().toSPARQL();
                    queryStr = queryStr.concat(subject + " " + st.getPredicate().toSPARQL() + " " + object + " . ");
                }
                it.close();
            }
        }
        queryStr = queryStr.concat("}");

        // empty query = is satisfied
        if (queryStr.equals("ASK {}"))
            return true;

        return model.sparqlAsk(queryStr);
    }

    /**
     * Checks if c2 is satisfied by c1, in other words, if doing an ASK sparql
     * query with all statements from c2 patterns into c1 pattern model returns
     * true
     * 
     * @param c1
     * @param c2
     * @return true if conditions are compatible
     */
    public boolean isConditionCompatible(Condition c1, Condition c2) {
        if (c1 == null || c2 == null)
            return false;

        Model m1 = patternToRDF2GoModel(c1.getPatternString());
        Model m2 = patternToRDF2GoModel(c2.getPatternString());
        if (m1.size() == m2.size()) {
            // create the ASK sparql query for a condition
            String queryStr = "ASK {";
            ClosableIterator<Statement> it = m2.iterator();
            for (; it.hasNext();) {
                Statement st = it.next();
                String su = (st.getSubject() instanceof BlankNode) ? toCleanVariable(st.getSubject().toString())
                        : st.getSubject().toSPARQL();
                String ob = (st.getObject() instanceof BlankNode) ? toCleanVariable(st.getObject().toString())
                        : st.getObject().toSPARQL();
                queryStr = queryStr.concat(su + " " + st.getPredicate().toSPARQL() + " " + ob + " . ");
            }
            it.close();
            queryStr = queryStr.concat("}");

            return m1.sparqlAsk(queryStr);
        }

        return false;
    }

    /**
     * Returns the subset of screens which are reachable within the given set. It receives a list of conditions
     * which are already satisfied (screenflow preconditions), which can satisfy screens' preconditions.
     * 
     * @param conditionList
     * @param screenList
     * @return reachable screens subset
     */
    public List<Screen> filterReachableScreens(List<Condition> conditionList, List<Screen> screenList) {
        ArrayList<Screen> results = new ArrayList<Screen>();
        ArrayList<Screen> toCheck = new ArrayList<Screen>();
        for (Screen screen : screenList) {
            if (screen.getPreconditions().isEmpty()) {
                results.add(screen);
            } else {
                toCheck.add(screen);
            }
        }
        if (!results.isEmpty() || !toCheck.isEmpty()) {
            results.addAll(filterReachableScreens(results, conditionList, toCheck));
        }
        return results;
    }

    protected List<Screen> filterReachableScreens(List<Screen> reachables, List<Condition> conditionList,
            List<Screen> screensToFilter) {
        ArrayList<Screen> results = new ArrayList<Screen>();
        ArrayList<Screen> toCheck = new ArrayList<Screen>();
        for (Screen screen : screensToFilter) {
            // check whether a list of preconditions is fulfilled => makes the screen reachable
            if (isConditionListSatisfied(reachables, conditionList, screen.getPreconditions(), true, true,
                    screen.getUri())) {
                results.add(screen);
            } else {
                toCheck.add(screen);
            }
        }
        // if there are new reachable screens and screens to check
        if (results.size() > 0 && toCheck.size() > 0) {
            reachables.addAll(results);
            results.addAll(filterReachableScreens(reachables, conditionList, toCheck));
        }
        return results;
    }

    protected String toCleanVariable(String name) {
        return "?" + name.replaceAll("[^a-zA-Z0-9]", "");
    }

    protected URI createClass(URI clazz) {
        tripleStore.addStatement(null, clazz, RDF.type, RDFS.Class);
        return clazz;
    }

    protected URI createClass(URI clazz, URI subClassOf) {
        tripleStore.addStatement(null, clazz, RDF.type, RDFS.Class);
        tripleStore.addStatement(null, clazz, RDFS.subClassOf, subClassOf);
        return clazz;
    }

    public void setLabel(URI clazz, String lang, String label) {
        tripleStore.addStatement(clazz, RDFS.label, tripleStore.createLanguageTagLiteral(label, lang));
    }

    public void setDescription(URI clazz, String lang, String description) {
        tripleStore.addStatement(clazz, DC.description, tripleStore.createLanguageTagLiteral(description, lang));
    }

    public void setTag(URI clazz, CTag tag) {
        BlankNode bnTag = tripleStore.createBlankNode();
        tripleStore.addStatement(clazz, CTAG.tagged, bnTag);

        if (tag instanceof AuthorCTag)
            tripleStore.addStatement(bnTag, RDF.type, CTAG.AuthorTag);
        else if (tag instanceof ReaderCTag)
            tripleStore.addStatement(bnTag, RDF.type, CTAG.ReaderTag);
        else if (tag instanceof AutoCTag)
            tripleStore.addStatement(bnTag, RDF.type, CTAG.AutoTag);
        else
            tripleStore.addStatement(bnTag, RDF.type, CTAG.Tag);

        if (tag.getMeans() != null)
            tripleStore.addStatement(bnTag, CTAG.means, tag.getMeans());
        for (String lang : tag.getLabels().keySet())
            tripleStore.addStatement(bnTag, CTAG.label,
                    tripleStore.createLanguageTagLiteral(tag.getLabels().get(lang), lang));
        if (tag.getTaggingDate() != null) {
            tripleStore.addStatement(bnTag, CTAG.taggingDate, tripleStore
                    .createDatatypeLiteral(DateFormatter.formatDateISO8601(tag.getTaggingDate()), XSD._date));
        } else { // no date provided, save the current date
            Date currentDate = new Date();
            tag.setTaggingDate(currentDate);
            tripleStore.addStatement(bnTag, CTAG.taggingDate,
                    tripleStore.createDatatypeLiteral(DateFormatter.formatDateISO8601(currentDate), XSD._date));
        }
    }

    public URI createConceptURI(String name, String domain) {
        return tripleStore
                .createURI(configuration.getURI(environment, "serverURL") + "/concepts/" + domain + "/" + name);
    }

    public boolean isConcept(URI concept) {
        return tripleStore.isClass(concept);
    }

    public URI createURIforBuildingBlock(URL namespace, String bb, URI ofClass, String id)
            throws DuplicatedException, OntologyInvalidException {
        URI bbUri = tripleStore.createURI(namespace.toString() + "/" + bb + "/" + id);
        if (containsBuildingBlock(bbUri))
            throw new DuplicatedException(bbUri + " already exists.");
        return bbUri;
    }

    protected URI createURIforBuildingBlock(URI ofClass, String id)
            throws DuplicatedException, OntologyInvalidException {
        if (ofClass.equals(FGO.ScreenFlow)) {
            return createURIforBuildingBlock(getServerURL(), "screen-flows", ofClass, id);
        } else if (ofClass.equals(FGO.Screen)) {
            return createURIforBuildingBlock(getServerURL(), "screens", ofClass, id);
        } else if (ofClass.equals(FGO.Form)) {
            return createURIforBuildingBlock(getServerURL(), "forms", ofClass, id);
        } else if (ofClass.equals(FGO.Operator)) {
            return createURIforBuildingBlock(getServerURL(), "operators", ofClass, id);
        } else if (ofClass.equals(FGO.BackendService)) {
            return createURIforBuildingBlock(getServerURL(), "services", ofClass, id);
        }
        return null;
    }

    protected Model getModelForBuildingBlock(URI uri) {
        Model model = RDF2Go.getModelFactory().createModel();
        model.open();
        ClosableIterator<Statement> bbIt = tripleStore.findStatements(uri, Variable.ANY, Variable.ANY);
        while (bbIt.hasNext()) {
            Statement stmt = bbIt.next();
            URI predicate = stmt.getPredicate();
            Node object = stmt.getObject();
            model.addStatement(stmt);
            if (predicate.equals(FGO.hasAction)) {
                model.addModel(getModelForAction(object.asURI()));
            } else if (predicate.equals(FGO.hasPreCondition)) {
                model.addModel(getModelForURI(object.asURI()));
            } else if (predicate.equals(FGO.hasPostCondition)) {
                model.addModel(getModelForURI(object.asURI()));
            } else if (predicate.equals(CTAG.tagged)) {
                model.addModel(getModelForBlankNode(object.asBlankNode()));
            } else if (predicate.equals(FGO.contains)) {
                model.addModel(getModelForURI(object.asURI()));
            } else if (predicate.equals(FGO.hasLibrary)) {
                model.addModel(getModelForBlankNode(object.asBlankNode()));
            }
        }
        return model;
    }

    protected Model getModelForAction(URI uri) {
        Model model = RDF2Go.getModelFactory().createModel();
        model.open();
        ClosableIterator<Statement> aIt = tripleStore.findStatements(uri, Variable.ANY, Variable.ANY);
        while (aIt.hasNext()) {
            Statement stmt = aIt.next();
            model.addStatement(stmt);
            if (stmt.getPredicate().equals(FGO.hasPreCondition)) {
                model.addModel(getModelForURI(stmt.getObject().asURI()));
            } else if (stmt.getPredicate().equals(FGO.uses)) {
                model.addModel(getModelForBlankNode(stmt.getObject().asBlankNode()));
            }
        }
        aIt.close();
        return model;
    }

    protected Model getModelForBlankNode(BlankNode bn) {
        Model model = RDF2Go.getModelFactory().createModel();
        model.open();
        ClosableIterator<Statement> cIt = tripleStore.findStatements(bn, Variable.ANY, Variable.ANY);
        while (cIt.hasNext()) {
            Statement st = cIt.next();
            Resource subject = st.getSubject();
            URI predicate = st.getPredicate();
            Node object = st.getObject();
            if (object instanceof DatatypeLiteral) {
                DatatypeLiteral literal = (DatatypeLiteral) object;
                model.addStatement(subject, predicate,
                        model.createDatatypeLiteral(literal.getValue(), literal.getDatatype()));
            } else if (object instanceof LanguageTagLiteral) {
                LanguageTagLiteral literal = (LanguageTagLiteral) object;
                model.addStatement(subject, predicate,
                        model.createLanguageTagLiteral(literal.getValue(), literal.getLanguageTag()));
            } else {
                model.addStatement(st);
            }
        }
        cIt.close();
        return model;
    }

    protected Model getModelForURI(URI uri) {
        Model model = RDF2Go.getModelFactory().createModel();
        model.open();
        ClosableIterator<Statement> cIt = tripleStore.findStatements(uri, Variable.ANY, Variable.ANY);
        while (cIt.hasNext()) {
            Statement st = cIt.next();
            Resource subject = st.getSubject();
            URI predicate = st.getPredicate();
            Node object = st.getObject();
            if (object instanceof DatatypeLiteral) {
                DatatypeLiteral literal = (DatatypeLiteral) object;
                model.addStatement(subject, predicate,
                        model.createDatatypeLiteral(literal.getValue(), literal.getDatatype()));
            } else if (object instanceof LanguageTagLiteral) {
                LanguageTagLiteral literal = (LanguageTagLiteral) object;
                model.addStatement(subject, predicate,
                        model.createLanguageTagLiteral(literal.getValue(), literal.getLanguageTag()));
            } else {
                model.addStatement(st);
            }
        }
        cIt.close();
        return model;
    }

    /**
     * Removes all the triples about the building block, its pre-/post-conditions, actions, etc.
     * 
     * @param bbUri
     * @throws NotFoundException
     */
    protected void removeBuildingBlock(URI bbUri) throws NotFoundException {
        if (!containsBuildingBlock(bbUri))
            throw new NotFoundException();

        ClosableIterator<Statement> bbIt = tripleStore.findStatements(bbUri, Variable.ANY, Variable.ANY);
        for (; bbIt.hasNext();) {
            Statement bbSt = bbIt.next();
            URI predicate = bbSt.getPredicate();
            Node object = bbSt.getObject();
            if (predicate.equals(FGO.hasAction)) {
                ClosableIterator<Statement> aIt = tripleStore.findStatements(object.asURI(), Variable.ANY,
                        Variable.ANY);
                for (; aIt.hasNext();) {
                    Statement aSt = aIt.next();
                    if (aSt.getPredicate().equals(FGO.hasPreCondition)) {
                        removeCondition(aSt.getObject().asURI());
                    }
                }
                aIt.close();
                tripleStore.removeResource(object.asURI());
            } else if (predicate.equals(FGO.hasPreCondition) || predicate.equals(FGO.hasPostCondition)) {
                removeCondition(object.asURI());
            } else if (predicate.equals(CTAG.tagged)) {
                tripleStore.removeResource(object.asBlankNode());
            }
        }
        bbIt.close();
        tripleStore.removeResource(bbUri);
        if (log.isInfoEnabled())
            log.info(bbUri + " removed.");
    }

    protected void removeCondition(URI cUri) throws NotFoundException {
        ClosableIterator<Statement> cIt = tripleStore.findStatements(cUri, Variable.ANY, Variable.ANY);
        for (; cIt.hasNext();) {
            Statement st = cIt.next();
            if (st.getPredicate().equals(FGO.hasPattern)) {
                tripleStore.removeModel(st.getObject().asURI());
                if (log.isInfoEnabled())
                    log.info("Pattern graph " + st.getObject().asURI() + " removed.");
            }
        }
        cIt.close();
        tripleStore.removeResource(cUri);
        if (log.isInfoEnabled())
            log.info(cUri + " removed.");
    }

    public void addScreenFlow(ScreenFlow sf) throws DuplicatedException, OntologyInvalidException,
            OntologyReadonlyException, NotFoundException, BuildingBlockException {
        URI sfUri = sf.getUri();
        if (sfUri != null) {
            if (containsBuildingBlock(sf))
                throw new DuplicatedException(sf.getUri() + " already exists.");
        } else {
            sfUri = createURIforBuildingBlock(FGO.ScreenFlow, sf.getId());
            sf.setUri(sfUri);
        }
        // sets current date if no date given
        if (sf.getCreationDate() == null)
            sf.setCreationDate(new Date());
        // persists the screen-flow
        saveScreenFlow(sf);
        // updates recommender
        screenRecommender.rebuild(); //FIXME do not rebuild every time a new screen-flow is added!
        if (log.isInfoEnabled())
            log.info("Screenflow " + sfUri + " added.");
    }

    protected void saveScreenFlow(ScreenFlow sf)
            throws OntologyReadonlyException, NotFoundException, BuildingBlockException {
        URI sfUri = sf.getUri();
        try {
            Model model = sf.toRDF2GoModel();
            tripleStore.addModel(model, prototypesGraph);
            processConditionsPatterns(model, prototypesGraph);
            model.close();
        } catch (Exception e) {
            log.error("Error while saving screen-flow " + sfUri, e);
            try {
                removeScreenFlow(sfUri);
            } catch (NotFoundException nfe) {
                log.error("Screen-flow " + sfUri + " does not exist.", nfe);
            }
            throw new BuildingBlockException("An error ocurred while saving the screen-flow: " + e, e);
        }
    }

    public void updateScreenFlow(ScreenFlow screenflow)
            throws NotFoundException, OntologyReadonlyException, BuildingBlockException {
        if (log.isInfoEnabled())
            log.info("Updating screen-flow " + screenflow.getUri() + "...");
        removeScreenFlow(screenflow.getUri());
        // save new content with the same URI
        saveScreenFlow(screenflow);
        // updates recommender
        screenRecommender.rebuild(); //FIXME do not rebuild every time a new screen-flow is updated!
        if (log.isInfoEnabled())
            log.info("Screen-flow " + screenflow.getUri() + " updated.");
    }

    /**
     * Delete a screen flow from the catalogue NOTE: Do NOT delete the screens
     * which is composed by.
     * 
     * @param sfUri
     *            the URI of the screen flow to be deleted
     * @throws NotFoundException
     */
    public void removeScreenFlow(URI sfUri) throws NotFoundException {
        removeBuildingBlock(sfUri);
        screenRecommender.rebuild(); //FIXME do not rebuild every time a new screen-flow is deleted!
    }

    public void addScreens(Screen... screens)
            throws DuplicatedException, OntologyInvalidException, BuildingBlockException {
        for (Screen screen : screens)
            addScreen(screen);
    }

    /**
     * Creates a new Screen into the catalogue
     * 
     * @param screen
     * @throws DuplicatedException
     * @throws OntologyInvalidException
     * @throws BuildingBlockException
     */
    public void addScreen(Screen screen)
            throws DuplicatedException, OntologyInvalidException, BuildingBlockException {
        URI screenUri = null;
        if (screen.getUri() != null) {
            screenUri = screen.getUri();
            if (containsBuildingBlock(screen))
                throw new DuplicatedException(screenUri + " already exists.");
        } else {
            screenUri = createURIforBuildingBlock(FGO.Screen, screen.getId());
            screen.setUri(screenUri);
        }
        // sets current date if no date given
        if (screen.getCreationDate() == null)
            screen.setCreationDate(new Date());
        // persists the screen
        saveScreen(screen);
        // create plans for the screen
        if (screenPlanner != null)
            screenPlanner.add(screen);
        // updates recommender
        scRecommender.rebuild(); //FIXME do not rebuild every time a new screen is added!
    }

    /**
     * Do not check if the screen already exists, and assumes the screen has a
     * well-formed unique URI To be invoked by addScreen and updateScreen
     * methods
     * 
     * @throws BuildingBlockException
     */
    protected void saveScreen(Screen screen) throws BuildingBlockException {
        URI sUri = screen.getUri();
        try {
            Model model = screen.toRDF2GoModel();
            tripleStore.addModel(model, prototypesGraph);
            processConditionsPatterns(model, prototypesGraph);
            model.close();
        } catch (Exception e) {
            log.error("Error while saving screen " + sUri, e);
            try {
                removeScreen(sUri);
            } catch (NotFoundException nfe) {
                log.error(nfe.toString(), nfe);
            }
            throw new BuildingBlockException("An error ocurred while saving the screen: " + e, e);
        }
    }

    public void updateScreen(Screen screen) throws NotFoundException, OntologyReadonlyException,
            RepositoryException, OntologyInvalidException, BuildingBlockException {
        if (log.isInfoEnabled())
            log.info("Updating screen " + screen.getUri() + "...");
        Screen oldScreen = getScreen(screen.getUri());
        // remove screen from the catalogue
        removeScreen(screen.getUri());
        // save new content with the same URI
        saveScreen(screen);
        // calculate new plans
        if (screenPlanner != null) {
            screenPlanner.update(screen, oldScreen);
        }
        scRecommender.rebuild(); //FIXME do not rebuild every time a screen is updated!
        if (log.isInfoEnabled())
            log.info("Screen " + screen.getUri() + " updated.");
    }

    public void removeScreen(URI screenUri) throws NotFoundException {
        removeBuildingBlock(screenUri);
        // remove the screen from the planner
        if (screenPlanner != null) {
            screenPlanner.remove(screenUri);
        }
        scRecommender.rebuild(); //FIXME do not rebuild every time a screen is deleted!
    }

    public URI cloneBuildingBlockByURI(URI bbUri) throws NotFoundException, BuildingBlockException {
        return cloneBuildingBlock(getBuildingBlock(bbUri));
    }

    //FIXME this method should be rethink! and implemented in a more maintainable and reusable way
    public URI cloneBuildingBlock(BuildingBlock bb) throws BuildingBlockException {
        String type = null;

        if (bb instanceof Screen)
            type = "screens";
        else if (bb instanceof Form)
            type = "forms";
        else if (bb instanceof Operator)
            type = "operators";
        else if (bb instanceof BackendService)
            type = "services";

        if (type == null)
            throw new BuildingBlockException(
                    "Building block must be a 'screen', 'form', 'operator' or 'backend service'.");

        // do a 'copy' of basic data of any building block
        URI copyUri = tripleStore.createUniqueUriWithName(configuration.getURI(environment, "serverURL"),
                "/" + type + "/clones/");
        String query = String.format("CONSTRUCT { <copy> ?p ?o } WHERE { GRAPH %s { <bb> ?p ?o } }",
                prototypesGraph.toSPARQL());
        query = query.replaceFirst("<copy>", copyUri.toSPARQL());
        query = query.replaceFirst("<bb>", bb.getUri().toSPARQL());
        ClosableIterator<Statement> it = tripleStore.sparqlConstruct(query).iterator();
        while (it.hasNext()) {
            Statement stmt = it.next();
            Resource subject = stmt.getSubject();
            URI predicate = stmt.getPredicate();
            Node object = stmt.getObject();
            if (predicate.equals(FGO.hasPreCondition) || predicate.equals(FGO.hasPostCondition)
                    || predicate.equals(FGO.hasAction)) {
                // these triples are created with specific URIs for the copy, so they are now discarded
            } else if (predicate.equals(FGO.contains)) {
                String str = object.asURI().toString();
                //FIXME pipes and triggers should not be saved from this triples, but the triples which points to other building blocks have to!
                if (!str.contains("/pipes/") && !str.contains("/triggers/")) {
                    tripleStore.addStatement(clonesGraph, subject, predicate, object);
                }
            } else if (predicate.equals(DC.date)) {
                // overrides the creation date
                tripleStore.addStatement(clonesGraph, copyUri, DC.date,
                        tripleStore.createDatatypeLiteral(DateFormatter.formatDateISO8601(new Date()), XSD._date));
            } else {
                tripleStore.addStatement(clonesGraph, subject, predicate, object);
            }
        }
        it.close();
        tripleStore.addStatement(clonesGraph, copyUri, FGO.hasPrototype, bb.getUri());
        tripleStore.addStatement(prototypesGraph, bb.getUri(), FGO.hasClone, copyUri);

        // adds data to the 'copy' based on the particular type of building block
        if (bb instanceof Screen) {
            Screen screen = (Screen) bb;
            // copy preconditions of the screen
            for (Condition condition : screen.getPreconditions()) {
                URI cUri = tripleStore.createURI(copyUri.toString() + "/preconditions/" + condition.getId());
                query = "CONSTRUCT { <copy> ?p ?o } WHERE { <bb> ?p ?o }";
                query = query.replaceFirst("<copy>", cUri.toSPARQL());
                query = query.replaceFirst("<bb>", condition.getUri().toSPARQL());
                it = tripleStore.sparqlConstruct(query).iterator();
                while (it.hasNext()) {
                    Statement stmt = it.next();
                    tripleStore.addStatement(clonesGraph, stmt.getSubject(), stmt.getPredicate(), stmt.getObject());
                }
                tripleStore.addStatement(clonesGraph, copyUri, FGO.hasPreCondition, cUri);
            }
            // copy postconditions of the screen
            for (Condition condition : screen.getPostconditions()) {
                URI cUri = tripleStore.createURI(copyUri.toString() + "/postconditions/" + condition.getId());
                query = "CONSTRUCT { <copy> ?p ?o } WHERE { <bb> ?p ?o }";
                query = query.replaceFirst("<copy>", cUri.toSPARQL());
                query = query.replaceFirst("<bb>", condition.getUri().toSPARQL());
                it = tripleStore.sparqlConstruct(query).iterator();
                while (it.hasNext()) {
                    Statement stmt = it.next();
                    tripleStore.addStatement(clonesGraph, stmt.getSubject(), stmt.getPredicate(), stmt.getObject());
                }
                tripleStore.addStatement(clonesGraph, copyUri, FGO.hasPostCondition, cUri);
            }
            for (Pipe pipe : screen.getPipes()) {
                URI pUri = tripleStore.createURI(copyUri + "/pipes/" + UUID.randomUUID().toString());
                Screen copy = BuildingBlockFactory.createScreen(copyUri);
                Pipe newPipe = pipe.clone(copy, pUri);
                tripleStore.addModel(newPipe.toRDF2GoModel(), clonesGraph);
                tripleStore.addStatement(clonesGraph, copyUri, FGO.contains, pUri);
            }
            for (Trigger trigger : screen.getTriggers()) {
                URI tUri = tripleStore.createURI(copyUri + "/triggers/" + UUID.randomUUID().toString());
                Screen copy = BuildingBlockFactory.createScreen(copyUri);
                Trigger newTrigger = trigger.clone(copy, tUri);
                tripleStore.addModel(newTrigger.toRDF2GoModel(), clonesGraph);
                tripleStore.addStatement(clonesGraph, copyUri, FGO.contains, tUri);
            }
        } else if (bb instanceof ScreenComponent) {
            ScreenComponent sc = (ScreenComponent) bb;
            // copy actions of the screen component
            for (Action action : sc.getActions()) {
                URI aUri = tripleStore.createURI(copyUri.toString() + "/actions/" + action.getName());
                query = "CONSTRUCT { <copy> ?p ?o } WHERE { <bb> ?p ?o }";
                query = query.replaceFirst("<copy>", aUri.toSPARQL());
                query = query.replaceFirst("<bb>", action.getUri().toSPARQL());
                it = tripleStore.sparqlConstruct(query).iterator();
                while (it.hasNext()) {
                    Statement stmt = it.next();
                    URI predicate = stmt.getPredicate();
                    if (predicate.equals(FGO.hasPreCondition)) {
                        // these triples are created with specific URIs for the copy, so they are now discarded
                    } else {
                        tripleStore.addStatement(clonesGraph, stmt.getSubject(), stmt.getPredicate(),
                                stmt.getObject());
                    }
                }
                tripleStore.addStatement(clonesGraph, copyUri, FGO.hasAction, aUri);
                // copy preconditions of the action
                for (Condition condition : action.getPreconditions()) {
                    URI cUri = tripleStore.createURI(aUri.toString() + "/preconditions/" + condition.getId());
                    query = "CONSTRUCT { <copy> ?p ?o } WHERE { <bb> ?p ?o }";
                    query = query.replaceFirst("<copy>", cUri.toSPARQL());
                    query = query.replaceFirst("<bb>", condition.getUri().toSPARQL());
                    it = tripleStore.sparqlConstruct(query).iterator();
                    while (it.hasNext()) {
                        Statement stmt = it.next();
                        tripleStore.addStatement(clonesGraph, stmt.getSubject(), stmt.getPredicate(),
                                stmt.getObject());
                    }
                    tripleStore.addStatement(clonesGraph, aUri, FGO.hasPreCondition, cUri);
                }
            }
            // copy postconditions of the screen component
            for (Condition condition : sc.getPostconditions()) {
                URI cUri = tripleStore.createURI(copyUri.toString() + "/postconditions/" + condition.getId());
                query = "CONSTRUCT { <copy> ?p ?o } WHERE { <bb> ?p ?o }";
                query = query.replaceFirst("<copy>", cUri.toSPARQL());
                query = query.replaceFirst("<bb>", condition.getUri().toSPARQL());
                it = tripleStore.sparqlConstruct(query).iterator();
                while (it.hasNext()) {
                    Statement stmt = it.next();
                    tripleStore.addStatement(clonesGraph, stmt.getSubject(), stmt.getPredicate(), stmt.getObject());
                }
                tripleStore.addStatement(clonesGraph, copyUri, FGO.hasPostCondition, cUri);
            }
        }

        return copyUri;
    }

    public void removeClone(URI uri) throws NotFoundException {
        ClosableIterator<Statement> cIt = tripleStore.findStatements(uri, Variable.ANY, Variable.ANY);
        for (; cIt.hasNext();) {
            Statement bbSt = cIt.next();
            URI predicate = bbSt.getPredicate();
            Node object = bbSt.getObject();
            if (predicate.equals(FGO.hasAction)) {
                ClosableIterator<Statement> aIt = tripleStore.findStatements(object.asURI(), Variable.ANY,
                        Variable.ANY);
                for (; aIt.hasNext();) {
                    Statement aSt = aIt.next();
                    if (aSt.getPredicate().equals(FGO.hasPreCondition)) {
                        tripleStore.removeResource(aSt.getObject().asURI());
                    }
                }
                aIt.close();
                tripleStore.removeResource(object.asURI());
            } else if (predicate.equals(FGO.hasPreCondition) || predicate.equals(FGO.hasPostCondition)) {
                tripleStore.removeResource(object.asURI());
            } else if (predicate.equals(FGO.contains)) {
                String str = object.asURI().toString();
                if (!str.contains("/pipes/") && !str.contains("/triggers/")) {
                    tripleStore.removeResource(object.asURI());
                }
            }
        }
        cIt.close();
        tripleStore.removeResource(uri);
    }

    public URI getPrototypeOfClone(URI clone) {
        ClosableIterator<Statement> cIt = tripleStore.findStatements(clone, FGO.hasPrototype, Variable.ANY);
        URI prototype = cIt.hasNext() ? cIt.next().getObject().asURI() : null;
        cIt.close();
        return prototype;
    }

    protected void addScreenComponent(URI type, ScreenComponent sc) throws DuplicatedException,
            OntologyInvalidException, InvalidBuildingBlockTypeException, BuildingBlockException {
        if (!type.equals(FGO.Form) && !type.equals(FGO.Operator) && !type.equals(FGO.BackendService))
            throw new InvalidBuildingBlockTypeException(type + " is not a screen component.");

        URI scUri = null;
        if (sc.getUri() != null) {
            scUri = sc.getUri();
            if (containsBuildingBlock(sc))
                throw new DuplicatedException(scUri + " already exists.");
        } else {
            scUri = createURIforBuildingBlock(type, sc.getId());
            sc.setUri(scUri);
        }
        // sets current date if no date given
        if (sc.getCreationDate() == null)
            sc.setCreationDate(new Date());
        // persists the screen component
        saveScreenComponent(sc);
    }

    protected void saveScreenComponent(ScreenComponent sc) throws BuildingBlockException {
        URI scUri = sc.getUri();
        try {
            Model model = sc.toRDF2GoModel();
            tripleStore.addModel(model, prototypesGraph);
            processConditionsPatterns(model, prototypesGraph);
            model.close();
        } catch (Exception e) {
            log.error("Error while saving screen component " + scUri, e);
            try {
                removeBuildingBlock(scUri);
            } catch (NotFoundException nfe) {
            }
            throw new BuildingBlockException("An error ocurred while saving the screen component: " + e, e);
        }
    }

    protected void updateScreenComponent(ScreenComponent sc) throws NotFoundException, BuildingBlockException {
        if (log.isInfoEnabled())
            log.info("Updating screen component " + sc.getUri() + "...");
        // remove old screen component from the catalogue
        removeScreenComponent(sc.getUri());
        // save new content with the same URI
        saveScreenComponent(sc);
        if (log.isInfoEnabled())
            log.info("Screen component " + sc.getUri() + " updated.");
    }

    protected void removeScreenComponent(URI scUri) throws NotFoundException {
        URI type = getType(scUri);
        if (type == null) {
            removeBuildingBlock(scUri);
            log.warn("Type is unknown: " + scUri + " cannot be removed.");
        } else {
            if (type.equals(FGO.Form))
                removeForm(scUri);
            else if (type.equals(FGO.Operator))
                removeOperator(scUri);
            else if (type.equals(FGO.BackendService))
                removeBackendService(scUri);
            else {
                removeBuildingBlock(scUri);
                log.warn(scUri
                        + " is not of type: Form, Operator or BackendService. Removing as generic building block.");
            }
        }
    }

    public void addForms(Form... forms) throws DuplicatedException, OntologyInvalidException,
            InvalidBuildingBlockTypeException, BuildingBlockException {
        for (Form form : forms)
            addForm(form);
    }

    public void addForm(Form fe) throws DuplicatedException, OntologyInvalidException,
            InvalidBuildingBlockTypeException, BuildingBlockException {
        addScreenComponent(FGO.Form, fe);
    }

    public void updateForm(Form fe) throws NotFoundException, BuildingBlockException {
        updateScreenComponent(fe);
    }

    public void removeForm(URI formUri) throws NotFoundException {
        removeBuildingBlock(formUri);
    }

    public void addOperators(Operator... operators) throws DuplicatedException, OntologyInvalidException,
            InvalidBuildingBlockTypeException, BuildingBlockException {
        for (Operator operator : operators)
            addOperator(operator);
    }

    public void addOperator(Operator op) throws DuplicatedException, OntologyInvalidException,
            InvalidBuildingBlockTypeException, BuildingBlockException {
        addScreenComponent(FGO.Operator, op);
    }

    public void updateOperator(Operator op) throws NotFoundException, BuildingBlockException {
        updateScreenComponent(op);
    }

    public void removeOperator(URI opUri) throws NotFoundException {
        removeBuildingBlock(opUri);
    }

    public void addBackendServices(BackendService... backendservices) throws DuplicatedException,
            OntologyInvalidException, InvalidBuildingBlockTypeException, BuildingBlockException {
        for (BackendService bs : backendservices)
            addBackendService(bs);
    }

    public void addBackendService(BackendService bs) throws DuplicatedException, OntologyInvalidException,
            InvalidBuildingBlockTypeException, BuildingBlockException {
        addScreenComponent(FGO.BackendService, bs);
    }

    public void updateBackendService(BackendService bs) throws NotFoundException, BuildingBlockException {
        updateScreenComponent(bs);
    }

    public void removeBackendService(URI bsUri) throws NotFoundException {
        removeBuildingBlock(bsUri);
    }

    public Collection<ScreenFlow> getAllScreenFlows() {
        return getAllScreenFlows(true);
    }

    public Collection<ScreenFlow> getAllScreenFlows(boolean isPrototype) {
        return getAllScreenFlows(isPrototype ? prototypesGraph : clonesGraph);
    }

    private Collection<ScreenFlow> getAllScreenFlows(URI graph) {
        LinkedList<ScreenFlow> results = new LinkedList<ScreenFlow>();
        ClosableIterator<Statement> it = tripleStore.findStatements(graph, Variable.ANY, RDF.type, FGO.ScreenFlow);
        while (it.hasNext())
            try {
                results.add(getScreenFlow(it.next().getSubject().asURI()));
            } catch (NotFoundException e) {
                log.error(e.getMessage(), e);
            }
        it.close();
        return results;
    }

    public Collection<Screen> getAllScreens() {
        return getAllScreens(true);
    }

    public Collection<Screen> getAllScreens(boolean isPrototype) {
        return getAllScreens(isPrototype ? prototypesGraph : clonesGraph);
    }

    private Collection<Screen> getAllScreens(URI graph) {
        LinkedList<Screen> results = new LinkedList<Screen>();
        ClosableIterator<Statement> it = tripleStore.findStatements(graph, Variable.ANY, RDF.type, FGO.Screen);
        while (it.hasNext()) {
            try {
                results.add(getScreen(it.next().getSubject().asURI()));
            } catch (NotFoundException e) {
                log.error(e.getMessage(), e);
            }
        }
        it.close();
        return results;
    }

    public Collection<Form> getAllForms() {
        return getAllForms(true);
    }

    public Collection<Form> getAllForms(boolean isPrototype) {
        return getAllForms(isPrototype ? prototypesGraph : clonesGraph);
    }

    private Collection<Form> getAllForms(URI graph) {
        LinkedList<Form> results = new LinkedList<Form>();
        ClosableIterator<Statement> it = tripleStore.findStatements(graph, Variable.ANY, RDF.type, FGO.Form);
        while (it.hasNext()) {
            try {
                results.add(getForm(it.next().getSubject().asURI()));
            } catch (NotFoundException e) {
                log.error(e.getMessage(), e);
            }
        }
        it.close();
        return results;
    }

    public Collection<Operator> getAllOperators() {
        return getAllOperators(true);
    }

    public Collection<Operator> getAllOperators(boolean isPrototype) {
        return getAllOperators(isPrototype ? prototypesGraph : clonesGraph);
    }

    private Collection<Operator> getAllOperators(URI graph) {
        LinkedList<Operator> results = new LinkedList<Operator>();
        ClosableIterator<Statement> it = tripleStore.findStatements(graph, Variable.ANY, RDF.type, FGO.Operator);
        while (it.hasNext()) {
            try {
                results.add(getOperator(it.next().getSubject().asURI()));
            } catch (NotFoundException e) {
                log.error(e.getMessage(), e);
            }
        }
        it.close();
        return results;
    }

    public Collection<BackendService> getAllBackendServices() {
        return getAllBackendServices(true);
    }

    public Collection<BackendService> getAllBackendServices(boolean isPrototype) {
        return getAllBackendServices(isPrototype ? prototypesGraph : clonesGraph);
    }

    private Collection<BackendService> getAllBackendServices(URI graph) {
        LinkedList<BackendService> results = new LinkedList<BackendService>();
        ClosableIterator<Statement> it = tripleStore.findStatements(graph, Variable.ANY, RDF.type,
                FGO.BackendService);
        while (it.hasNext()) {
            try {
                results.add(getBackendService(it.next().getSubject().asURI()));
            } catch (NotFoundException e) {
                log.error(e.getMessage(), e);
            }
        }
        it.close();
        return results;
    }

    public Collection<URI> getAllConcepts(String[] tags) {
        LinkedList<URI> results = new LinkedList<URI>();
        String queryString = "SELECT DISTINCT ?concept \n" + "WHERE {\n" + " { { ?concept " + RDF.type.toSPARQL()
                + " " + RDFS.Class.toSPARQL() + " } UNION { " + " ?concept " + RDF.type.toSPARQL() + " "
                + OWL.Class.toSPARQL() + " } } ";
        if (tags != null && tags.length > 0) {
            queryString = queryString.concat("{");
            for (String tag : tags) {
                queryString = queryString.concat(
                        " { ?concept " + CTAG.tagged.toSPARQL() + " ?ctag . " + " ?ctag " + CTAG.label.toSPARQL()
                                + " ?tag . " + " FILTER(regex(str(?tag), \"" + tag + "\", \"i\")) } UNION");
            }
            queryString = queryString.substring(0, queryString.length() - 5); // remove last 'UNION'
            queryString = queryString.concat("} . ");
        }
        queryString = queryString.concat("}");
        QueryResultTable qrt = tripleStore.sparqlSelect(queryString);
        ClosableIterator<QueryRow> itResults = qrt.iterator();
        while (itResults.hasNext()) {
            QueryRow qr = itResults.next();
            Node node = qr.getValue("concept");

            if (node instanceof URI) { // some ontologies (ie: DBPedia) adds some concepts as Blank Nodes
                results.add(qr.getValue("concept").asURI());
            }
        }
        itResults.close();

        return results;
    }

    public BuildingBlock getBuildingBlock(URI uri) throws NotFoundException {
        if (isType(uri, FGO.ScreenFlow))
            return getScreenFlow(uri);
        else if (isType(uri, FGO.Screen))
            return getScreen(uri);
        else if (isType(uri, FGO.Form))
            return getForm(uri);
        else if (isType(uri, FGO.Operator))
            return getOperator(uri);
        else if (isType(uri, FGO.BackendService))
            return getBackendService(uri);
        return null;
    }

    public boolean isType(URI uri, URI type) {
        URI t = getType(uri);
        if (t != null && t.equals(type))
            return true;
        return false;
    }

    /**
     * Returns the first type (rdf:type) found for an URI
     * 
     * @param uri
     * @return type URI
     */
    public URI getType(URI uri) {
        ClosableIterator<Statement> it = tripleStore.findStatements(uri, RDF.type, Variable.ANY);
        URI type = null;
        if (it.hasNext()) {
            type = it.next().getObject().asURI();
        }
        it.close();
        return type;
    }

    public ScreenFlow getScreenFlow(URI uri) throws NotFoundException {
        if (!containsBuildingBlock(uri))
            throw new NotFoundException();
        return BuildingBlockRDF2GoBuilder.buildScreenFlow(getModelForBuildingBlock(uri));
    }

    public Screen getScreen(URI uri) throws NotFoundException {
        if (!containsBuildingBlock(uri))
            throw new NotFoundException();
        return BuildingBlockRDF2GoBuilder.buildScreen(getModelForBuildingBlock(uri));
    }

    public ScreenComponent getScreenComponent(URI uri) throws NotFoundException {
        URI type = getType(uri);
        if (type == null)
            return null;
        if (type.equals(FGO.Form))
            return getForm(uri);
        else if (type.equals(FGO.Operator))
            return getOperator(uri);
        else if (type.equals(FGO.BackendService))
            return getBackendService(uri);
        return null;
    }

    public Form getForm(URI uri) throws NotFoundException {
        if (!containsBuildingBlock(uri))
            throw new NotFoundException();
        return BuildingBlockRDF2GoBuilder.buildForm(getModelForBuildingBlock(uri));
    }

    public Operator getOperator(URI uri) throws NotFoundException {
        if (!containsBuildingBlock(uri))
            throw new NotFoundException();
        return BuildingBlockRDF2GoBuilder.buildOperator(getModelForBuildingBlock(uri));
    }

    public BackendService getBackendService(URI uri) throws NotFoundException {
        if (!containsBuildingBlock(uri))
            throw new NotFoundException();
        return BuildingBlockRDF2GoBuilder.buildBackendService(getModelForBuildingBlock(uri));
    }

    protected void processConditionsPatterns(Model model, URI graphURI) {
        ClosableIterator<Statement> it = model.iterator();
        for (; it.hasNext();) {
            Statement stmt = it.next();
            if (stmt.getPredicate().equals(FGO.hasPatternString)
                    && isConditionPositive(model, stmt.getSubject().asURI())) {
                URI pUri = tripleStore.createUniqueUriWithName(configuration.getURI(environment, "serverURL"),
                        "/patterns/");
                String pattern = stmt.getObject().asDatatypeLiteral().getValue();
                Model patternModel = patternToRDF2GoModel(pattern);
                tripleStore.addStatement(graphURI, stmt.getSubject(), FGO.hasPattern, pUri);
                tripleStore.addModel(patternModel, pUri);

                // checks/imports the ontologies/vocabularies used in the condition pattern
                if (configuration.getBoolean(this.environment, "import-ontologies")) {
                    importMissingOntologies(patternModel);
                }
            }
        }
        it.close();
    }

    protected boolean isConditionPositive(Model model, URI conUri) {
        boolean isPositive = false;
        ClosableIterator<Statement> it = model.findStatements(conUri, FGO.isPositive, Variable.ANY);
        if (it.hasNext()) {
            Statement stmt = it.next();
            isPositive = Boolean.parseBoolean(stmt.getObject().asDatatypeLiteral().getValue());
        }
        it.close();
        return isPositive;
    }

    public Model patternToRDF2GoModel(String pattern) {
        Model model = RDF2Go.getModelFactory().createModel();
        model.open();
        HashMap<String, BlankNode> blankNodes = new HashMap<String, BlankNode>();

        StringTokenizer tokens = new StringTokenizer(pattern);// , " . ");
        for (; tokens.hasMoreTokens();) {
            String subject = tokens.nextToken();
            String predicate = tokens.nextToken();
            String object = tokens.nextToken();
            if (tokens.hasMoreTokens())
                tokens.nextToken(); // discard the .
            // gets if exists or creates the subject
            BlankNode subjectNode = blankNodes.get(subject);
            if (subjectNode == null) {
                subjectNode = model.createBlankNode();
                blankNodes.put(subject, subjectNode);
            }
            // creates a URI or BlankNode for the object
            Node objectNode;
            try {
                objectNode = tripleStore.createURI(object);
            } catch (IllegalArgumentException e) {
                objectNode = blankNodes.get(object);
                if (objectNode == null) {
                    objectNode = model.createBlankNode();
                    blankNodes.put(subject, subjectNode);
                }
            }
            model.addStatement(subjectNode, tripleStore.createURI(predicate), objectNode);
        }

        return model;
    }

    public Concept getConcept(URI uri) {
        Concept concept = BuildingBlockFactory.createConcept();
        concept.setUri(uri);

        // fill the information about the concept
        ClosableIterator<Statement> cIt = tripleStore.findStatements(uri, Variable.ANY, Variable.ANY);
        if (!cIt.hasNext()) // the resource does not exist
            return null;
        for (; cIt.hasNext();) {
            Statement st = cIt.next();
            URI predicate = st.getPredicate();
            Node object = st.getObject();
            if (predicate.equals(RDFS.subClassOf) && object instanceof URI) {
                concept.setSubClassOf(object.asURI());
            } else if (predicate.equals(RDFS.label) && object instanceof LanguageTagLiteral) {
                LanguageTagLiteral label = object.asLanguageTagLiteral();
                concept.getLabels().put(label.getLanguageTag(), label.getValue());
            } else if (predicate.equals(DC.description) && object instanceof LanguageTagLiteral) {
                LanguageTagLiteral description = object.asLanguageTagLiteral();
                concept.getDescriptions().put(description.getLanguageTag(), description.getValue());
            } else if (predicate.equals(CTAG.tagged) && object instanceof BlankNode) {
                CTag tag = new CTag();
                ClosableIterator<Statement> tagIt = tripleStore.findStatements(object.asBlankNode(), Variable.ANY,
                        Variable.ANY);
                for (; tagIt.hasNext();) {
                    Statement tagSt = tagIt.next();
                    URI tagPredicate = tagSt.getPredicate();
                    Node tagObject = tagSt.getObject();
                    if (tagPredicate.equals(CTAG.means)) {
                        tag.setMeans(tagObject.asURI());
                    } else if (tagPredicate.equals(CTAG.label)) {
                        if (tagObject instanceof LanguageTagLiteral) {
                            LanguageTagLiteral label = tagObject.asLanguageTagLiteral();
                            tag.getLabels().put(label.getLanguageTag(), label.getValue());
                        }
                    } else if (tagPredicate.equals(CTAG.taggingDate)) {
                        tag.setTaggingDate(
                                DateFormatter.parseDateISO8601(tagObject.asDatatypeLiteral().getValue()));
                    }
                }
                concept.getTags().add(tag);
            }
        }
        cIt.close();

        // read its attributes (properties)
        concept.getAttributes().addAll(attributesFor(concept));

        return concept;
    }

    protected Property getAttribute(URI uri) {
        Property att = BuildingBlockFactory.createAttribute();
        att.setUri(uri);

        ClosableIterator<Statement> cIt = tripleStore.findStatements(uri, Variable.ANY, Variable.ANY);
        if (!cIt.hasNext()) // the resource does not exist
            return null;
        for (; cIt.hasNext();) {
            Statement st = cIt.next();
            URI predicate = st.getPredicate();
            Node object = st.getObject();
            if (predicate.equals(RDFS.subPropertyOf) && object instanceof URI) {
                att.setSubPropertyOf(object.asURI());
            } else if (predicate.equals(RDFS.range) && object instanceof URI) {
                att.setType(object.asURI());
            }
        }
        cIt.close();

        return att;
    }

    public List<Property> attributesFor(Concept concept) {
        LinkedList<Property> attList = new LinkedList<Property>();
        ClosableIterator<Statement> cIt = tripleStore.findStatements(Variable.ANY, RDFS.domain, concept.getUri());
        for (; cIt.hasNext();) {
            Statement st = cIt.next();
            Property att = getAttribute(st.getSubject().asURI());
            att.setConcept(concept);
            attList.add(att);
        }
        cIt.close();
        return attList;
    }

    public void addConcept(Concept concept) throws DuplicatedException, BuildingBlockException {
        URI cUri = concept.getUri();
        if (containsConcept(cUri)) {
            throw new DuplicatedException(cUri + " already exists.");
        }
        // persists the concept
        if (!saveConcept(concept)) {
            throw new BuildingBlockException(
                    "An error ocurred while saving the concept. Please, ensure the concept is well defined.");
        }
    }

    // FIXME: throw an exception instead of boolean
    protected boolean saveConcept(Concept concept) {
        URI cUri = concept.getUri();
        Model model = null;
        try {
            model = concept.toRDF2GoModel();
            tripleStore.addModel(model);
            model.close();
            return true;
        } catch (Exception e) {
            log.error("Error while saving concept " + cUri, e);
            try {
                removeConcept(cUri);
            } catch (NotFoundException nfe) {
                log.error("Concept " + cUri + " does not exist.", nfe);
            }
        } finally {
            if (model != null)
                model.close();
        }
        return false;
    }

    public void updateConcept(Concept concept) throws NotFoundException, BuildingBlockException {
        if (log.isInfoEnabled())
            log.info("Updating concept " + concept.getUri() + "...");
        removeConcept(concept.getUri());
        // save new content with the same URI
        if (saveConcept(concept)) {
            if (log.isInfoEnabled())
                log.info("Concept " + concept.getUri() + " updated.");
        } else {
            throw new BuildingBlockException(
                    "An error ocurred while saving the concept. Please, ensure the concept is well defined.");
        }
    }

    /**
     * Remove a concept given its URI. Only removes the triples which start with
     * the given URI or as its context URI, if there are more triples with this
     * URI as object, they will remain.
     * 
     * @param uri
     * @throws NotFoundException
     */
    public void removeConcept(URI uri) throws NotFoundException {
        tripleStore.removeModel(uri);
        tripleStore.removeResource(uri);
    }

    /**
     * 
     * @param cloneGoal
     * @param cloneList list of 'cloned' screens
     * @return list of plans
     * @throws BuildingBlockException
     * @throws NotFoundException 
     */
    public List<Plan> searchPlans(URI cloneGoal, List<Screen> cloneList)
            throws BuildingBlockException, NotFoundException {
        URI prototypeGoal = getPrototypeOfClone(cloneGoal);
        if (prototypeGoal == null) {
            throw new BuildingBlockException(cloneGoal + " must be a clone of a prototype.");
        }

        ArrayList<Screen> prototypeList = new ArrayList<Screen>();
        for (Screen screen : cloneList) {
            if (screen.getPrototype() == null) {
                throw new BuildingBlockException(screen.getUri() + " must be a clone of a prototype.");
            }
            Screen s = getScreen(screen.getPrototype());
            if (!prototypeList.contains(s)) {
                prototypeList.add(s);
            }
        }

        List<Plan> planList = new ArrayList<Plan>();
        if (screenPlanner != null) {
            planList.addAll(screenPlanner.searchPlans(prototypeGoal, prototypeList));
        }

        return planList;
    }

    /**
     * 
     * @param cloneGoalList
     * @param cloneList
     * @return list of plans
     * @throws BuildingBlockException 
     * @throws NotFoundException 
     */
    public List<Plan> searchPlans(List<URI> cloneGoalList, List<Screen> cloneList)
            throws BuildingBlockException, NotFoundException {
        ArrayList<URI> prototypeGoalList = new ArrayList<URI>();
        for (URI cloneGoal : cloneGoalList) {
            URI prototypeGoal = getPrototypeOfClone(cloneGoal);
            if (prototypeGoal == null) {
                throw new BuildingBlockException(cloneGoal + " must be a clone of a prototype.");
            }
            prototypeGoalList.add(prototypeGoal);
        }

        ArrayList<Screen> prototypeList = new ArrayList<Screen>();
        for (Screen screen : cloneList) {
            if (screen.getPrototype() == null) {
                throw new BuildingBlockException(screen.getUri() + " must be a clone of a prototype.");
            }
            Screen s = getScreen(screen.getPrototype());
            if (!prototypeList.contains(s)) {
                prototypeList.add(s);
            }
        }

        List<Plan> planList = new ArrayList<Plan>();
        if (screenPlanner != null) {
            planList.addAll(screenPlanner.searchPlans(prototypeGoalList, prototypeList));
        }

        return planList;
    }

    /**
     * Says whether a given object is an URI
     * @param o the object to be checked
     * @return true if the object is an URI
     */
    public boolean isURI(Object o) {
        return o instanceof URI;
    }

    /**
     * Imports into the catalogue any ontology which is being
     * used in the given model, in the form of a concept or a
     * property for example.
     * Ex: if the model contains the URI http://xmlns.com/foaf/0.1/Person,
     * the FOAF (http://xmlns.com/foaf/0.1) ontology will be imported.
     * @param model set of triples used to check for URIs
     */
    protected void importMissingOntologies(Model model) {
        ClosableIterator<Statement> cIt = model.iterator();
        for (; cIt.hasNext();) {
            Statement stmt = cIt.next();
            Resource subject = stmt.getSubject();
            URI predicate = stmt.getPredicate();
            Node object = stmt.getObject();
            if (isURI(subject)) {
                importOntology(subject.asURI());
            }
            importOntology(predicate);
            if (isURI(object)) {
                importOntology(object.asURI());
            }
        }
        cIt.close();
    }

    /**
     * Figures out the ontology URI for a given concept or property URI, then it
     * fetches and store the ontology in the catalogue.
     * @param classOrPropertyURI
     */
    protected void importOntology(URI classOrPropertyURI) {
        // checks if there's already an ontology defining the class or property
        for (URI oUri : getAllOntologies()) {
            if (classOrPropertyURI.toString().startsWith(oUri.toString()))
                return;
        }

        // try to fetch the URI in order to find the ontology content, as it should
        // regarding the best practices of publishing ontologies
        Ontology ontology = OntologyFetcher.fetch(MiscUtil.RDF2GoURItoURL(classOrPropertyURI));
        if (ontology == null) {
            // no ontology found, we try to check if Sindice knows where to find it
            OntologyFinder finder = new SindiceOntologyFinder();
            URI oUri = finder.find(classOrPropertyURI);
            if (oUri != null) {
                ontology = OntologyFetcher.fetch(MiscUtil.RDF2GoURItoURL(oUri));
            }
        }
        if (ontology != null) {
            if (addOntology(ontology)) {
                if (log.isInfoEnabled())
                    log.info("Ontology " + ontology.getUri() + " has been added.");
            }
        }
    }

    public Collection<Sample> getAllSamples() {
        return getAllSamples(null);
    }

    public Collection<Sample> getAllSamples(URI classUri) {
        UriOrVariable object = classUri == null ? Variable.ANY : classUri;
        LinkedList<Sample> results = new LinkedList<Sample>();
        ClosableIterator<Statement> it = tripleStore.findStatements(Variable.ANY, RDF.type, object);
        Sample sample;
        while (it.hasNext()) {
            Resource subject = it.next().getSubject();
            if (isURI(subject) && subject.toString().startsWith(getServerURL() + "/samples/")) {
                sample = getSample(subject.asURI());
                if (sample != null)
                    results.add(sample);
            }
        }
        it.close();
        return results;
    }

    public Sample getSample(URI uri) {
        return SampleRDF2GoBuilder.buildSample(getModelForURI(uri));
    }

    public Sample addSample(Sample sample) {
        URI uri = tripleStore.createUniqueUriWithName(MiscUtil.URLtoRDF2GoURI(getServerURL()), "/samples/");
        sample.setUri(uri);
        return saveSample(sample) ? sample : null;
    }

    // FIXME: throw an exception instead of boolean
    protected boolean saveSample(Sample sample) {
        URI uri = sample.getUri();
        Model model = null;
        try {
            model = sample.toRDF2GoModel();
            tripleStore.addModel(model);
            model.close();
            return true;
        } catch (Exception e) {
            log.error(e.toString(), e);
            try {
                removeSample(uri);
            } catch (NotFoundException nfe) {
                log.error(nfe.toString(), nfe);
            }
        } finally {
            if (model != null)
                model.close();
        }
        return false;
    }

    public void updateSample(Sample sample) throws BuildingBlockException, NotFoundException {
        removeSample(sample.getUri());
        if (!saveSample(sample)) {
            throw new BuildingBlockException(
                    "An error ocurred while saving the sample. Please, ensure the sample is well defined.");
        }
    }

    public void removeSample(URI uri) throws NotFoundException {
        tripleStore.removeResource(uri);
    }

    public Collection<IServeResponse> findIServeWS(List<Condition> conList) {
        if (conList.isEmpty())
            return new LinkedList<IServeResponse>();

        IServeClient client = new IServeClient(new IServeConfiguration("iserve.properties"));
        ArrayList<URI> list = new ArrayList<URI>();
        for (Condition con : conList) {
            ClosableIterator<Statement> it = patternToRDF2GoModel(con.getPatternString())
                    .findStatements(Variable.ANY, RDF.type, Variable.ANY);
            for (; it.hasNext();) {
                Node object = it.next().getObject();
                if (object instanceof URI)
                    list.add(object.asURI());
            }
            it.close();
        }

        return client.query(list);
    }

    // TODO only for debug purposes
    public TripleStore getTripleStore() {
        return tripleStore;
    }

    // TODO only for debug purposes
    public void exportToTrig(String filePath) {
        try {
            tripleStore.export(new FileOutputStream(filePath), Syntax.Trig);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    // TODO only for debug purposes
    public void printStatements() {
        ClosableIterator<Statement> it = tripleStore.findStatements(Variable.ANY, Variable.ANY, Variable.ANY);
        for (; it.hasNext();) {
            Statement st = it.next();
            System.out.println(
                    st.getContext() + " - " + st.getSubject() + " - " + st.getPredicate() + " - " + st.getObject());
        }
    }

    // TODO only for debug purposes
    public void dump() {
        tripleStore.dump();
    }

}