/****************************************************************************** * * Copyright 2014 Paphus Solutions Inc. * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ package org.botlibre.knowledge; import java.io.Serializable; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import org.botlibre.Bot; import org.botlibre.api.knowledge.Data; import org.botlibre.api.knowledge.Network; import org.botlibre.api.knowledge.Relationship; import org.botlibre.api.knowledge.Vertex; /** * An interconnected set of vertices, * representing and knowledge-space. * Basic implementation to allow subclasses to avoid defining some of the basic stuff. */ public class BasicNetwork extends AbstractNetwork implements Serializable { private static final long serialVersionUID = 1L; protected Network parent; protected Set<Vertex> verticies = null; protected Map<Number, Vertex> verticiesById = null; protected static long nextId() { return nextId++; } public BasicNetwork() { this(false); } public BasicNetwork(boolean isShortTerm) { super(isShortTerm); this.verticies = new HashSet<Vertex>(); this.verticiesById = new HashMap<Number, Vertex>(); this.verticiesByData = new HashMap<Object, Vertex>(); } public BasicNetwork(Network parent) { this(true); this.parent = parent; } protected void addRelationship(Relationship relationship) { // Nothing required by default. } public void resume() { getBot().log(this, "Resuming", Bot.FINE, this); Set<Vertex> newVerticies = new HashSet<Vertex>(Math.max(this.verticies.size(), MAX_SIZE)); // Shrink to fixed size. int level = 1; while ((this.verticies.size() > MAX_SIZE) && (level < 256)) { Iterator<Vertex> iterator = this.verticies.iterator(); while ((this.verticies.size() > MAX_SIZE) && iterator.hasNext()) { Vertex vertex = iterator.next(); if ((!vertex.isPrimitive()) && vertex.getConsciousnessLevel() <= level) { iterator.remove(); } } level = level * 2; } newVerticies.addAll(this.verticies); // Reset originals and clear relationships. if (getParent() != null) { for (Vertex vertex : newVerticies) { Vertex original = getParent().findById(vertex.getId()); vertex.setOriginal(original); } } // Reset id hashes. this.verticies = newVerticies; this.verticiesById = new HashMap<Number, Vertex>(); for (Vertex vertex : newVerticies) { this.verticiesById.put(vertex.getId(), vertex); } this.verticiesByData = new HashMap<Object, Vertex>(); for (Vertex vertex : newVerticies) { if (vertex.getData() != null) { this.verticiesByData.put(vertex.getData(), vertex); } } } /** * Merge the memory into the long term. * This is similar to a transactional commit. * The changes should also be persisted, as the long term should always just be a cache of the storage. * This implementation does not support persistence. */ public void save() { getBot().log(this, "Saving", Bot.FINE, this); getParent().merge(this); } /** * Return a thread safe copy of the network. */ public synchronized BasicNetwork clone() { BasicNetwork clone = (BasicNetwork)super.clone(); clone.setVerticies(new HashSet<Vertex>(getVerticies())); clone.setVerticiesById(new HashMap<Number, Vertex>(getVerticiesById())); clone.setVerticiesByData(new HashMap<Object, Vertex>(getVerticiesByData())); return clone; } /** * Clear all vertices from the network. */ public synchronized void clear() { this.verticies = new HashSet<Vertex>(); this.verticiesById = new HashMap<Number, Vertex>(); this.verticiesByData = new HashMap<Object, Vertex>(); } /** * Add the existing vertex to the network. * Used to load an existing vertex, createVertex must be used to create a new one. */ public synchronized void addVertex(Vertex vertex) { if (vertex.getId() != null) { // Ensure the nextId sequence is consistent when restoring the network from storage. if (nextId <= vertex.getId().longValue()) { nextId = vertex.getId().longValue() + 1; } getVerticiesById().put(vertex.getId(), vertex); } if (vertex.getData() != null) { getVerticiesByData().put(vertex.getData(), vertex); } getVerticies().add(vertex); ((BasicVertex) vertex).setNetwork(this); } protected Set<Vertex> getVerticies() { return verticies; } protected void setVerticies(Set<Vertex> verticies) { this.verticies = verticies; } public Network getParent() { return parent; } public void setParent(Network parent) { this.parent = parent; } public int size() { return getVerticies().size(); } /** * Remove the vertex and all references to it from the network. */ public void removeVertexAndReferences(Vertex vertex) { Vertex managed = findById(vertex.getId()); if (managed == null) { return; } Iterator<Relationship> iterator = findAllRelationshipsTo(vertex).iterator(); // Remove all references. while (iterator.hasNext()) { Relationship relationship = iterator.next(); relationship.getSource().internalRemoveRelationship(relationship); } removeVertex(vertex); } /** * Remove the vertex from the network. * Note that the vertex must be no longer referenced by any other vertex in the network. */ public void removeVertex(Vertex vertex) { getVerticies().remove(vertex.getId()); if (vertex.hasData()) { getVerticiesByData().remove(vertex.getData()); } } /** * Return count of all vertices. */ public int countAll() { return findAll().size(); } /** * Return count of all vertices matching the query. * Currently unable to process in memory. */ public int countAllLike(String filter) { return findAllLike(filter).size(); } /** * Return all vertices. */ public List<Vertex> findAll() { return new ArrayList<Vertex>(getVerticies()); } /** * Return all vertices. */ public List<Vertex> findAll(int pageSize, int page) { return findAll(); } /** * Return all vertices matching the query. * Currently unable to process in memory. */ public List<Vertex> findAllQuery(String query) { return new ArrayList<Vertex>(); } /** * Return all vertices matching the query. * Currently unable to process in memory. */ @SuppressWarnings("rawtypes") public List<Vertex> findAllQuery(String query, Map parameters, int pageSize, int page) { return new ArrayList<Vertex>(); } /** * Return all vertices matching the query. * Currently unable to process in memory. */ public List<Vertex> findAllQuery(String query, int max) { return new ArrayList<Vertex>(); } /** * Execute the native query. */ @SuppressWarnings("rawtypes") public List findByNativeQuery(String sql, Class type, int max) { return new ArrayList<Vertex>(); } /** * Execute and commit the native query. */ public int executeNativeQuery(String sql) { return 0; } /** * Execute and commit the update query. */ public int executeQuery(String jpql) { return 0; } /** * Return all vertices matching the filter. */ public List<Vertex> findAllLike(String filter) { Pattern pattern = Pattern.compile(filter.replace("*", ".*")); List<Vertex> results = new ArrayList<Vertex>(); for (Vertex vertex : findAll()) { if (vertex.hasData()) { if (pattern.matcher(vertex.getDataValue()).matches()) { results.add(vertex); } } } return results; } /** * Return all vertices matching the filter. */ public List<Vertex> findAllLike(String filter, int pageSize, int page) { return findAllLike(filter); } /** * Return the vertex with the given data. */ public synchronized Vertex findByData(Object data) { if (data == null) { return null; } Vertex vertex = (Vertex) getVerticiesByData().get(data); // If not local, lookup in parent and cloned into local. if ((vertex == null) && (getParent() != null)) { Vertex originalVertex = getParent().findByData(data); if (originalVertex != null) { vertex = new BasicVertex(originalVertex); addVertex(vertex); vertex.incrementAccessCount(); } } return vertex; } /** * Return the lob data. */ public synchronized Data findData(Data data) { return data; } /** * Return the vertex with the given name. */ public synchronized Vertex findByName(String name) { if (name == null) { return null; } for (Vertex vertex : findAll()) { if (name.equals(vertex.getName())) { return vertex; } } return null; } /** * Return the vertex with the given name. */ public synchronized Vertex findById(Number id) { if (id == null) { return null; } Vertex vertex = (Vertex) getVerticiesById().get(id); // If not local, lookup in parent and cloned into local. if ((vertex == null) && (getParent() != null)) { Vertex originalVertex = getParent().findById(id); if (originalVertex != null) { vertex = new BasicVertex(originalVertex); addVertex(vertex); } } return vertex; } /** * Return a query builder. */ public CriteriaBuilder getCriteriaBuilder() { return null; } /** * Find all relationships related to the vertex or of the vertex type. */ public synchronized List<Vertex> findAllInstances(Vertex type, Vertex relationship, Calendar start) { return new ArrayList<Vertex>(); } /** * Execute the criteria query. */ @SuppressWarnings({ "rawtypes" }) public synchronized List search(CriteriaQuery criteria, int page, int max) { return new ArrayList<Vertex>(); } /** * Find all relationships related to the vertex or of the vertex relationship type. */ public synchronized List<Relationship> findAllRelationshipsTo(Vertex vertex) { List<Relationship> relationships = new ArrayList<Relationship>(); Iterator<Vertex> iterator = findAll().iterator(); // Remove all references. while (iterator.hasNext()) { Vertex next = iterator.next(); Iterator<Relationship> allRelationships = next.allRelationships(); while (allRelationships.hasNext()) { Relationship relationship = allRelationships.next(); if (relationship.getTarget() == vertex) { relationships.add(relationship); } else if (relationship.getType() == vertex) { relationships.add(relationship); } } } return relationships; } /** * Find all relationships related to the vertex by the vertex type. */ public synchronized List<Relationship> findAllRelationshipsTo(Vertex vertex, Vertex type) { List<Relationship> relationships = new ArrayList<Relationship>(); Iterator<Vertex> iterator = findAll().iterator(); // Remove all references. while (iterator.hasNext()) { Vertex next = iterator.next(); Collection<Relationship> allRelationships = next.getRelationships(type); if (allRelationships != null) { for (Relationship relationship : allRelationships) { if (relationship.getTarget() == vertex) { relationships.add(relationship); } } } } return relationships; } public Map<Number, Vertex> getVerticiesById() { return verticiesById; } public void setVerticiesById(Map<Number, Vertex> verticiesById) { this.verticiesById = verticiesById; } public String toString() { return getClass().getSimpleName() + "(" + size()+ ")"; } }