001/*
002 * (C) Copyright 2006-2007 Nuxeo SAS <http://nuxeo.com> and others
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Jean-Marc Orliaguet, Chalmers
011 *
012 * $Id$
013 */
014
015package org.nuxeo.theme.relations;
016
017import java.util.ArrayList;
018import java.util.Collection;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022import java.util.Random;
023
024import org.nuxeo.theme.Registrable;
025
026public final class RelationStorage implements Registrable {
027
028    private static final Map<Long, Relation> relationsMap = new HashMap<Long, Relation>();
029
030    private static final Map<List<String>, List<Long>> relatesMap = new HashMap<List<String>, List<Long>>();
031
032    private static final String[][] WILDCARDS_MONADIC = { { "" } };
033
034    private static final String[][] WILDCARDS_DYADIC = { { "", "" }, { "*", "" }, { "", "*" } };
035
036    private static final String[][] WILDCARDS_TRIADIC = { { "", "", "" }, { "*", "", "" }, { "", "*", "" },
037            { "", "", "*" }, { "*", "*", "" }, { "", "*", "*" }, { "*", "", "*" } };
038
039    public synchronized void add(Relation relation) {
040        Long key = findFreeKey();
041        relationsMap.put(key, relation);
042        index(key, relation);
043    }
044
045    public synchronized void remove(Relation relation) {
046        List<String> indexString = computeIndexString(relation.getRelates());
047        for (Long id : relatesMap.get(indexString)) {
048            relationsMap.remove(id);
049        }
050        relatesMap.remove(indexString);
051    }
052
053    public Collection<Relation> search(Predicate predicate, List<Relate> relates) {
054        List<Relation> relations = new ArrayList<Relation>();
055        List<String> indexString = computeIndexString(relates);
056        if (!relatesMap.containsKey(indexString)) {
057            return relations;
058        }
059        for (Long id : relatesMap.get(indexString)) {
060            Relation relation = relationsMap.get(id);
061            if (relation == null) {
062                continue;
063            }
064            if (!relation.hasPredicate(predicate)) {
065                continue;
066            }
067            relations.add(relationsMap.get(id));
068        }
069        return relations;
070    }
071
072    public Collection<Relation> search(Predicate predicate, Relate first) {
073        List<Relate> relates = new ArrayList<Relate>();
074        relates.add(first);
075        return search(predicate, relates);
076    }
077
078    public Collection<Relation> search(Predicate predicate, Relate first, Relate second) {
079        List<Relate> relates = new ArrayList<Relate>();
080        relates.add(first);
081        relates.add(second);
082        return search(predicate, relates);
083    }
084
085    public Collection<Relation> search(Predicate predicate, Relate first, Relate second, Relate third) {
086        List<Relate> relates = new ArrayList<Relate>();
087        relates.add(first);
088        relates.add(second);
089        relates.add(third);
090        return search(predicate, relates);
091    }
092
093    public Collection<Relation> list() {
094        return relationsMap.values();
095    }
096
097    public synchronized void clear() {
098        relationsMap.clear();
099        relatesMap.clear();
100    }
101
102    private synchronized Long findFreeKey() {
103        Long key;
104        Random generator = new Random();
105        do {
106            key = generator.nextLong();
107        } while (relationsMap.containsKey(key));
108        return key;
109    }
110
111    private synchronized void index(Long id, Relation relation) {
112        List<Relate> relates = relation.getRelates();
113        String[][] wildcards = null;
114
115        Integer arity = relates.size();
116        if (arity == 1) {
117            wildcards = WILDCARDS_MONADIC;
118        } else if (arity == 2) {
119            wildcards = WILDCARDS_DYADIC;
120        } else if (arity == 3) {
121            wildcards = WILDCARDS_TRIADIC;
122        }
123        assert wildcards != null;
124
125        for (String[] wildcard : wildcards) {
126            List<String> indexString = new ArrayList<String>();
127            for (Integer j = 0; j < arity; j++) {
128                if (wildcard[j].equals("*")) {
129                    indexString.add("*");
130                } else {
131                    indexString.add(relates.get(j).hash());
132                }
133            }
134            List<Long> ids;
135            if (relatesMap.containsKey(indexString)) {
136                ids = relatesMap.get(indexString);
137            } else {
138                ids = new ArrayList<Long>();
139            }
140            ids.add(id);
141            relatesMap.put(indexString, ids);
142        }
143    }
144
145    private List<String> computeIndexString(List<Relate> relates) {
146        List<String> indexString = new ArrayList<String>();
147        for (Relate relate : relates) {
148            if (relate == null || relate.hash() == null) {
149                indexString.add("*");
150            } else {
151                indexString.add(relate.hash());
152            }
153        }
154        return indexString;
155    }
156
157}