/****************************************************************************** * * 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.StringWriter; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Level; import org.botlibre.Bot; import org.botlibre.aiml.AIMLParser; import org.botlibre.api.knowledge.Network; import org.botlibre.api.knowledge.Relationship; import org.botlibre.api.knowledge.Vertex; import org.botlibre.self.SelfByteCodeCompiler; import org.botlibre.self.SelfCompiler; import org.botlibre.self.SelfParseException; import org.botlibre.sense.text.TextEntry; import org.botlibre.thought.language.Language; import org.botlibre.util.TextStream; import org.botlibre.util.Utils; /** * An interconnected set of vertices, * representing and knowledge-space. * Basic implementation to allow subclasses to avoid defining some of the basic stuff. */ public abstract class AbstractNetwork implements Network, Cloneable { /** Define max text size for data value. */ public static final int MAX_TEXT = 1000; public static int MAX_SIZE = 500; protected static long nextId = 0; protected static long nextId() { return nextId++; } protected boolean isShortTerm; protected Map<Object, Vertex> verticiesByData = null; /** Back reference to Bot instance. **/ protected Bot bot; public AbstractNetwork(boolean isShortTerm) { this.isShortTerm = isShortTerm; this.verticiesByData = new HashMap<Object, Vertex>(); } public boolean isReadOnly() { return false; } protected Map<Object, Vertex> getVerticiesByData() { return verticiesByData; } protected void setVerticiesByData(Map<Object, Vertex> verticiesByData) { this.verticiesByData = verticiesByData; } /** * Save the property setting to the current transaction. */ public void saveProperty(String propertyName, String value, boolean startup) { this.bot.memory().setProperty(propertyName, value); } /** * Remove the property setting to the current transaction. */ public void removeProperty(String propertyName) { this.bot.memory().removeProperty(propertyName); } /** * Return a copy of the network. */ public synchronized AbstractNetwork clone() { AbstractNetwork clone = null; try { clone = (AbstractNetwork)super.clone(); } catch (CloneNotSupportedException exception) { throw new Error(exception); } return clone; } public String toString() { StringWriter writer = new StringWriter(); writer.write(getClass().getSimpleName()); writer.write("("); writer.write(String.valueOf(size())); writer.write(")"); return writer.toString(); } public abstract void addVertex(Vertex vertex); protected abstract void addRelationship(Relationship relationship); /** * Create a new vertex in this network, * assign the id and creation date. */ public synchronized Vertex createVertex() { BasicVertex vertex = new BasicVertex(); vertex.init(); addVertex(vertex); return vertex; } /** * Create a temporary, non-persistent vertex. */ public Vertex createTemporyVertex() { BasicVertex vertex = new BasicVertex(); vertex.setIsTemporary(true); vertex.setNetwork(this); return vertex; } /** * Create a new vertex from the source. * The source is from another network. */ public synchronized Vertex createVertex(Vertex source) { Vertex vertex = findById(source.getId()); if (vertex == null) { vertex = findByData(source.getData()); } if (vertex == null) { vertex = new BasicVertex(); vertex.setName(source.getName()); vertex.setData(source.getData()); vertex.setAccessCount(source.getAccessCount()); vertex.setAccessDate(source.getAccessDate()); vertex.setPinned(source.isPinned()); vertex.setCreationDate(source.getCreationDate()); vertex.setConsciousnessLevel(source.getConsciousnessLevel()); addVertex(vertex); // Set id from database. source.setId(vertex.getId()); } return vertex; } /** * Create a new vertex in this network with the data, * If a vertex with the data already exists, then it is returned as the data must be unique. */ @SuppressWarnings("rawtypes") public synchronized Vertex createVertex(Object data) { if ((data instanceof String) && ((String)data).length() > MAX_TEXT) { data = ((String)data).substring(0, MAX_TEXT); } if (data instanceof Class) { data = new Primitive(((Class)data).getName()); } Vertex vertex = findByData(data); Vertex meaning = null; Vertex similar = null; if ((vertex == null) && (data instanceof String)) { // Perform case insensitive lookup, and associate the same meaning. String word = ((String)data).toLowerCase(); if (!word.equals(data)) { similar = findByData(word); } if (similar == null) { word = Utils.capitalize((String)data); if (!word.equals(data)) { similar = findByData(word); } } if (similar == null) { word = ((String)data).toUpperCase(); if (!word.equals(data)) { similar = findByData(word); } } if (similar != null) { meaning = similar.getRelationship(Primitive.MEANING); } } if (vertex == null) { vertex = new BasicVertex(); vertex.init(); vertex.setData(data); addVertex(vertex); if (meaning != null) { vertex.addRelationship(Primitive.MEANING, meaning); meaning.addWeakRelationship(Primitive.WORD, vertex, 0.2f); // Associate word type from similar word. Collection<Relationship> relationships = similar.getRelationships(Primitive.INSTANTIATION); if (relationships != null) { for (Relationship type : relationships) { if (!type.isInverse()) { vertex.addRelationship(Primitive.INSTANTIATION, type.getTarget()); } } } } } // TODO: better way/place to do this if ((data instanceof BigInteger) && !vertex.instanceOf(Primitive.INTEGER)) { boolean isNegative = false; vertex.addRelationship(Primitive.INSTANTIATION, Primitive.THING); vertex.addRelationship(Primitive.INSTANTIATION, Primitive.NUMBER); vertex.addRelationship(Primitive.INSTANTIATION, Primitive.INTEGER); String digits = data.toString(); int length = digits.length(); for (int index = 0; index < length; index++) { String digit = digits.substring(length - index - 1, length - index); if (digit.equals("-")) { vertex.addRelationship(Primitive.INSTANTIATION, Primitive.NEGATIVE); isNegative = true; continue; } if (Character.isDigit(digit.charAt(0))) { vertex.addRelationship(Primitive.DIGIT, createVertex(new BigInteger(digit)), index); } } Vertex word = createWord(digits); word.addRelationship(Primitive.MEANING, vertex); vertex.addRelationship(Primitive.WORD, word); if (!isNegative) { char last = digits.charAt(digits.length() - 1); Vertex ordinal = null; if (last == '1') { ordinal = createWord(digits + "st"); } else if (last == '2') { ordinal = createWord(digits + "nd"); } else if (last == '3') { ordinal = createWord(digits + "rd"); } else { ordinal = createWord(digits + "th"); } ordinal.addRelationship(Primitive.MEANING, vertex); vertex.addRelationship(Primitive.ORDINAL, ordinal); word = createWord("+" + digits); word.addRelationship(Primitive.MEANING, vertex); vertex.addWeakRelationship(Primitive.WORD, word, 0.2f); } } if ((data instanceof BigDecimal) && !vertex.instanceOf(Primitive.DECIMAL)) { boolean isNegative = false; vertex.addRelationship(Primitive.INSTANTIATION, Primitive.THING); vertex.addRelationship(Primitive.INSTANTIATION, Primitive.NUMBER); vertex.addRelationship(Primitive.INSTANTIATION, Primitive.DECIMAL); String digits = ((BigDecimal) data).toPlainString(); int length = digits.length(); boolean decimal = false; for (int index = 0; index < length; index++) { String digit = digits.substring(length - index - 1, length - index); if (digit.equals(".")) { decimal = true; continue; } if (digit.equals("-")) { vertex.addRelationship(Primitive.INSTANTIATION, Primitive.NEGATIVE); isNegative = true; continue; } if (Character.isDigit(digit.charAt(0))) { if (decimal) { vertex.addRelationship(Primitive.DECIMAL, createVertex(new BigInteger(digit)), index); } else { vertex.addRelationship(Primitive.INTEGER, createVertex(new BigInteger(digit)), index); } } } Vertex word = createWord(digits); word.addRelationship(Primitive.MEANING, vertex); vertex.addRelationship(Primitive.WORD, word); if (!isNegative) { word = createWord("+" + digits); word.addRelationship(Primitive.MEANING, vertex); vertex.addWeakRelationship(Primitive.WORD, word, 0.2f); } } if (data instanceof Time) { Time time = (Time)data; vertex.addRelationship(Primitive.INSTANTIATION, Primitive.TIME); String text = Utils.printTime(time, "h:mm:ss a"); Vertex word = createWord(text); word.addRelationship(Primitive.MEANING, vertex); vertex.addRelationship(Primitive.WORD, word); Calendar calendar = Calendar.getInstance(); calendar.setTime(time); int hour = calendar.get(Calendar.HOUR); if (hour == 0) { hour = 12; } vertex.addRelationship(Primitive.HOUR, createVertex(BigInteger.valueOf(hour))); vertex.addRelationship(Primitive.MINUTE, createVertex(BigInteger.valueOf(calendar.get(Calendar.MINUTE)))); vertex.addRelationship(Primitive.SECOND, createVertex(BigInteger.valueOf(calendar.get(Calendar.SECOND)))); if (calendar.get(Calendar.AM_PM) == Calendar.PM) { vertex.addRelationship(Primitive.AM_PM, Primitive.PM); } else { vertex.addRelationship(Primitive.AM_PM, Primitive.AM); } } if (data instanceof java.sql.Date) { java.sql.Date date = (java.sql.Date)data; vertex.addRelationship(Primitive.INSTANTIATION, Primitive.DATE); String text = Utils.printDate(date, "EEEE MMMM d y"); Vertex word = createWord(text); word.addRelationship(Primitive.MEANING, vertex); vertex.addRelationship(Primitive.WORD, word); Calendar calendar = Calendar.getInstance(); calendar.setTime(date); vertex.addRelationship(Primitive.DAY_OF_YEAR, createVertex(BigInteger.valueOf(calendar.get(Calendar.DAY_OF_YEAR)))); vertex.addRelationship(Primitive.DAY, createVertex(BigInteger.valueOf(calendar.get(Calendar.DATE)))); vertex.addRelationship(Primitive.DAY_OF_WEEK, createVertex(Primitive.DAYS_OF_WEEK[calendar.get(Calendar.DAY_OF_WEEK) - 1])); vertex.addRelationship(Primitive.MONTH, createVertex(Primitive.MONTHS[calendar.get(Calendar.MONTH)])); vertex.addRelationship(Primitive.YEAR, createVertex(BigInteger.valueOf(calendar.get(Calendar.YEAR)))); } return vertex; } /** * Merge the vertices and relations of the network into this network. */ // Obsolete with DatabaseMemory public synchronized void merge(Network network) { for (Vertex vertex : network.findAll()) { merge(vertex); } } /** * Merge the vertex into this network. */ // Obsolete with DatabaseMemory public synchronized void merge(Vertex sourceVertex) { Vertex targetVertex = createVertex(sourceVertex); if (targetVertex.getId() == null) { // Don't assign id until merge so other networks can assign differently. targetVertex.setId(new Long(nextId())); sourceVertex.setId(new Long(nextId())); } else if (sourceVertex.getId() == null) { // Must assign id, so source can be associated with target. sourceVertex.setId(targetVertex.getId()); } if (sourceVertex.hasRelationships()) { List<Relationship> targetRelationships = new ArrayList<Relationship>(); // Add and update relationships. for (Iterator<Relationship> relationships = sourceVertex.allRelationships(); relationships.hasNext();) { Relationship sourceRelationship = relationships.next(); Vertex targetRelationshipType = createVertex(sourceRelationship.getType()); Vertex targetRelationshipTarget = createVertex(sourceRelationship.getTarget()); Relationship targetRelationship = targetVertex.addRelationship(targetRelationshipType, targetRelationshipTarget, sourceRelationship.getIndex(), true); if (!sourceRelationship.isInverse()) { targetRelationship.setCorrectness(Math.max(targetRelationship.getCorrectness(), sourceRelationship.getCorrectness())); } else { targetRelationship.setCorrectness(Math.min(targetRelationship.getCorrectness(), sourceRelationship.getCorrectness())); } targetRelationship.setIndex(sourceRelationship.getIndex()); addRelationship(targetRelationship); targetRelationships.add(targetRelationship); } if (sourceVertex.totalRelationships() != targetVertex.totalRelationships()) { targetVertex.getRelationships().clear(); // Remove the removed relationships. for (Relationship targetRelationship : targetRelationships) { targetVertex.addRelationship(targetRelationship, true); } } } if (!targetVertex.isPrimitive()) { targetVertex.setAccessCount(sourceVertex.getAccessCount()); targetVertex.setAccessDate(sourceVertex.getAccessDate()); targetVertex.setPinned(sourceVertex.isPinned()); targetVertex.setConsciousnessLevel(sourceVertex.getConsciousnessLevel()); } } /** * Find the vertex matching the source, or create a new one. * This is used from importing another memory. */ public synchronized Vertex importVertex(Vertex source, Map<Vertex, Vertex> identitySet) { Vertex target = identitySet.get(source); if (target != null) { return target; } target = findByData(source.getData()); if (target == null) { target = findByName(source.getName()); if (target != null) { // If the source is not the same classification as the target, then may be something different. for (Relationship classification : target.getRelationships(Primitive.INSTANTIATION)) { if (!source.hasRelationship(createVertex(Primitive.INSTANTIATION), classification.getTarget())) { target = null; break; } } } if (target == null) { target = new BasicVertex(); target.setName(source.getName()); target.setData(source.getData()); target.setAccessCount(source.getAccessCount()); target.setAccessDate(source.getAccessDate()); target.setPinned(source.isPinned()); target.setCreationDate(source.getCreationDate()); target.setConsciousnessLevel(source.getConsciousnessLevel()); addVertex(target); } } identitySet.put(source, target); return target; } /** * Merge the vertex into this network from an import. * This is used from importing another memory. */ public synchronized void importMerge(Vertex source, Map<Vertex, Vertex> identitySet) { Vertex target = importVertex(source, identitySet); for (Iterator<Relationship> relationships = source.allRelationships(); relationships.hasNext();) { Relationship sourceRelationship = relationships.next(); Vertex targetRelationshipType = importVertex(sourceRelationship.getType(), identitySet); Vertex targetRelationshipTarget = importVertex(sourceRelationship.getTarget(), identitySet); Relationship targetRelationship = target.addRelationship(targetRelationshipType, targetRelationshipTarget, sourceRelationship.getIndex(), true); if (!sourceRelationship.isInverse()) { targetRelationship.setCorrectness(Math.max(targetRelationship.getCorrectness(), sourceRelationship.getCorrectness())); } else { targetRelationship.setCorrectness(Math.min(targetRelationship.getCorrectness(), sourceRelationship.getCorrectness())); } targetRelationship.setIndex(sourceRelationship.getIndex()); addRelationship(targetRelationship); } } /** * Remove the relationship from the network. * Note that the relationship must be no longer referenced by any other vertex in the network. */ public void removeRelationship(Relationship relationship) { // Nothing required by default. } /** * Return the relationship meta vertex. */ public Vertex createMeta(Relationship relationship) { Vertex meta = relationship.getMeta(); if (meta == null) { meta = createInstance(Primitive.META); meta.setType("Meta"); relationship.setMeta(meta); } return meta; } /** * Create a new instance of the type. */ public Vertex createInstance(Primitive type) { return createInstance(createVertex(type)); } /** * Create a new instance of the type. */ public Vertex createInstance(Vertex type) { Vertex instance = createVertex(); instance.addRelationship(Primitive.INSTANTIATION, type); return instance; } /** * Tokenize the text into its words and create a vertex representation of the word or compound word. */ public synchronized Vertex createWord(String text) { text = text.trim(); List<String> wordsText = Utils.getWords(text); // Allow # to represent a primitive. if ((wordsText.size() == 1) && (text.length() > 0) && (text.charAt(0) == '#')) { Vertex primitive = createVertex(new Primitive(text)); Vertex word = createVertex(text); word.addRelationship(Primitive.INSTANTIATION, Primitive.WORD); word.addRelationship(Primitive.MEANING, primitive); return word; } Vertex compoundWord = createVertex(text); // Check if is a compound word. if (wordsText.size() > 1) { compoundWord.addRelationship(Primitive.INSTANTIATION, Primitive.COMPOUND_WORD); compoundWord.addRelationship(Primitive.INSTANTIATION, Primitive.WORD); for (int index = 0; index < wordsText.size(); index++) { String wordText = wordsText.get(index); Vertex word = createVertex(wordText); word.addRelationship(Primitive.INSTANTIATION, Primitive.WORD); // Only associate first word for indexing. if (index == 0) { word.addRelationship(Primitive.COMPOUND_WORD, compoundWord); } compoundWord.addRelationship(Primitive.WORD, word, index); } } else { compoundWord.addRelationship(Primitive.INSTANTIATION, Primitive.WORD); } return compoundWord; } /** * Create the word as a name. */ public synchronized Vertex createName(String text) { Vertex name = createWord(text); name.addRelationship(Primitive.INSTANTIATION, Primitive.NAME); return name; } /** * Associate alternative cases of the word with the meaning. */ public synchronized void associateCaseInsensitivity(String word, Vertex meaning) { Collection<Relationship> classifications = createVertex(word).getRelationships(Primitive.INSTANTIATION); String lower = word.toLowerCase(); Vertex capWord = createWord(Utils.capitalize(lower)); capWord.addRelationship(Primitive.MEANING, meaning); meaning.addWeakRelationship(Primitive.WORD, capWord, 0.2f); if (classifications != null) { for (Relationship classification : classifications) { if (!classification.isInverse()) { capWord.addRelationship(Primitive.INSTANTIATION, classification.getTarget()); } } } Vertex upperWord = createWord(lower.toUpperCase()); upperWord.addRelationship(Primitive.MEANING, meaning); meaning.addWeakRelationship(Primitive.WORD, upperWord, 0.2f); if (classifications != null) { for (Relationship classification : classifications) { if (!classification.isInverse()) { upperWord.addRelationship(Primitive.INSTANTIATION, classification.getTarget()); } } } Vertex lowerWord = createWord(lower); lowerWord.addRelationship(Primitive.MEANING, meaning); meaning.addWeakRelationship(Primitive.WORD, lowerWord, 0.2f); if (classifications != null) { for (Relationship classification : classifications) { if (!classification.isInverse()) { lowerWord.addRelationship(Primitive.INSTANTIATION, classification.getTarget()); } } } } /** * Associate alternative cases of the word with the meaning, types, conjugations. */ public synchronized void associateCaseInsensitivity(Vertex word) { Collection<Relationship> meanings = word.getRelationships(Primitive.MEANING); Collection<Relationship> classifications = word.getRelationships(Primitive.INSTANTIATION); Collection<Relationship> types = word.getRelationships(Primitive.TYPE); Collection<Relationship> tenses = word.getRelationships(Primitive.TENSE); Collection<Relationship> conjugations = word.getRelationships(Primitive.CONJUGATION); String lower = word.getDataValue().toLowerCase(); String caps = Utils.capitalize(lower); Vertex capWord = createWord(caps); if (meanings != null) { for (Relationship meaning : meanings) { if (!meaning.isInverse()) { capWord.addRelationship(Primitive.MEANING, meaning.getTarget()); meaning.getTarget().addWeakRelationship(Primitive.WORD, capWord, 0.2f); } } } if (classifications != null) { for (Relationship classification : classifications) { if (!classification.isInverse()) { capWord.addRelationship(Primitive.INSTANTIATION, classification.getTarget()); } } } if (types != null) { for (Relationship type : types) { if (!type.isInverse()) { capWord.addRelationship(Primitive.TYPE, type.getTarget()); } } } if (tenses != null) { for (Relationship tense : tenses) { if (!tense.isInverse()) { capWord.addRelationship(Primitive.TENSE, tense.getTarget()); } } } if (conjugations != null) { for (Relationship conjugation : conjugations) { if (!conjugation.isInverse()) { capWord.addRelationship(Primitive.CONJUGATION, conjugation.getTarget()); } } } Vertex upperWord = createWord(lower.toUpperCase()); if (meanings != null) { for (Relationship meaning : meanings) { if (!meaning.isInverse()) { upperWord.addRelationship(Primitive.MEANING, meaning.getTarget()); meaning.getTarget().addWeakRelationship(Primitive.WORD, upperWord, 0.2f); } } } if (classifications != null) { for (Relationship classification : classifications) { if (!classification.isInverse()) { upperWord.addRelationship(Primitive.INSTANTIATION, classification.getTarget()); } } } if (types != null) { for (Relationship type : types) { if (!type.isInverse()) { upperWord.addRelationship(Primitive.TYPE, type.getTarget()); } } } if (tenses != null) { for (Relationship tense : tenses) { if (!tense.isInverse()) { upperWord.addRelationship(Primitive.TENSE, tense.getTarget()); } } } if (conjugations != null) { for (Relationship conjugation : conjugations) { if (!conjugation.isInverse()) { upperWord.addRelationship(Primitive.CONJUGATION, conjugation.getTarget()); } } } Vertex lowerWord = createWord(lower); if (meanings != null) { for (Relationship meaning : meanings) { if (!meaning.isInverse()) { lowerWord.addRelationship(Primitive.MEANING, meaning.getTarget()); meaning.getTarget().addWeakRelationship(Primitive.WORD, lowerWord, 0.2f); } } } if (classifications != null) { for (Relationship classification : classifications) { if (!classification.isInverse()) { lowerWord.addRelationship(Primitive.INSTANTIATION, classification.getTarget()); } } } if (types != null) { for (Relationship type : types) { if (!type.isInverse()) { lowerWord.addRelationship(Primitive.TYPE, type.getTarget()); } } } if (tenses != null) { for (Relationship tense : tenses) { if (!tense.isInverse()) { lowerWord.addRelationship(Primitive.TENSE, tense.getTarget()); } } } if (conjugations != null) { for (Relationship conjugation : conjugations) { if (!conjugation.isInverse()) { lowerWord.addRelationship(Primitive.CONJUGATION, conjugation.getTarget()); } } } } /** * Convert the sentence to a paragraph if it has multiple phrases. */ public Vertex createParagraph(Vertex sentence) { if (sentence.hasRelationship(Primitive.INSTANTIATION, Primitive.PARAGRAPH) && sentence.hasRelationship(Primitive.SENTENCE)) { return sentence; } if (sentence.hasInverseRelationship(Primitive.INSTANTIATION, Primitive.PARAGRAPH)) { return sentence; } if (sentence.getData() instanceof String) { return createParagraph(sentence.getDataValue()); } return sentence; } /** * Tokenize the paragraph into its sentences and create a vertex representation. */ public Vertex createParagraph(String text) { TextStream stream = new TextStream(text); String current = stream.nextSentence(); Vertex paragraph = null; Vertex previous = null; int index = 0; while (current != null) { if (current.length() > MAX_TEXT) { current = current.substring(0, MAX_TEXT); } Vertex sentence = createSentence(current); sentence.removeRelationship(Primitive.INSTANTIATION, Primitive.PARAGRAPH); String next = stream.nextSentence(); if (paragraph == null) { if (next == null) { return sentence; } if (text.length() > MAX_TEXT) { paragraph = createVertex(); } else { paragraph = createVertex(text); if (paragraph.hasRelationship(Primitive.INSTANTIATION, Primitive.PARAGRAPH) && paragraph.hasRelationship(Primitive.SENTENCE)) { return paragraph; } } paragraph.addRelationship(Primitive.INSTANTIATION, Primitive.PARAGRAPH); } paragraph.addRelationship(Primitive.SENTENCE, sentence, index); boolean learnGrammar = true; Language language = getBot().mind().getThought(Language.class); if (language != null) { learnGrammar = language.getLearnGrammar(); } if (learnGrammar) { sentence.addRelationship(Primitive.PARAGRAPH, paragraph); if (previous != null) { sentence.addRelationship(Primitive.PREVIOUS, previous); previous.addRelationship(Primitive.NEXT, sentence); } } previous = sentence; current = next; index++; } if (paragraph == null) { return createSentence(""); } return paragraph; } /** * Tokenize the sentence into its words and create a vertex representation. */ public Vertex createSentence(String text) { return createSentence(text, false); } /** * Tokenize the sentence pattern into its words and wildcrads, and create a vertex representation. */ public Vertex createPattern(String text) { return createPattern(text, SelfCompiler.getCompiler()); } /** * Tokenize the sentence pattern into its words and wildcrads, and create a vertex representation. */ public Vertex createPattern(String text, SelfCompiler compiler) { if (text.indexOf('"') != -1) { text = text.replace("\"", "\"\""); } String code = null; if (compiler.getVersion() <= 2) { code = "Pattern:\"" + text + "\""; } else { code = "Pattern(\"" + text + "\")"; } Vertex pattern = null; if (text.length() < MAX_TEXT) { pattern = createVertex(code); if (pattern.instanceOf(Primitive.PATTERN)) { Collection<Relationship> words = pattern.getRelationships(Primitive.WORD); if (words != null && words.size() == pattern.getWordCount()) { return pattern; } } } else { pattern = createVertex(); pattern.setName(text); } pattern.addRelationship(Primitive.INSTANTIATION, Primitive.PATTERN); int index = 0; TextStream stream = new TextStream(text); String special = "*_#^$[]{}"; Vertex list = null; int listindex = 0; boolean precedence = false; Map<String, Map<String, Vertex>> elements = null; while (!stream.atEnd()) { String word = stream.nextWord(); if (word != null) { Vertex element = null; if (word.equals("\\")) { String escape = stream.nextWord(); if (escape != null && (special.indexOf(escape) != -1)) { word = escape; element = createWord(word); element.addRelationship(Primitive.PATTERN, pattern); } else { element = createWord(word); element.addRelationship(Primitive.PATTERN, pattern); if (list != null) { list.addRelationship(Primitive.ELEMENT, element, listindex); listindex++; } else { pattern.addRelationship(Primitive.WORD, element, index); index++; } word = escape; element = createWord(word); element.addRelationship(Primitive.PATTERN, pattern); } } else if (word.equals("$")) { precedence = true; continue; } else if (word.equals("*")) { element = createVertex(Primitive.WILDCARD); } else if (word.equals("_")) { element = createVertex(Primitive.UNDERSCORE); } else if (word.equals("^")) { element = createVertex(Primitive.HATWILDCARD); } else if (word.startsWith("#")) { if (word.length() > 1) { String name = word.substring(1, word.length()); element = createVertex(new Primitive(name)); } else { element = createVertex(Primitive.POUNDWILDCARD); } } else if (word.equals("[") || word.equals("(")) { element = createInstance(Primitive.ARRAY); if (word.equals("[")) { element.addRelationship(Primitive.TYPE, Primitive.REQUIRED); } list = element; listindex = 0; } else if (word.equals("]") || word.equals(")")) { list = null; continue; } else if (word.equals("{")) { if (elements == null) { elements = compiler.buildElementsMap(this); } element = compiler.parseElement(stream, elements, false, this); stream.skipWhitespace(); compiler.ensureNext('}', stream); } else { element = createWord(word); element.addRelationship(Primitive.PATTERN, pattern); } if (list != null && element != list) { list.addRelationship(Primitive.ELEMENT, element, listindex); listindex++; } else { Relationship relationship = pattern.addRelationship(Primitive.WORD, element, index); if (precedence) { createMeta(relationship).addRelationship(Primitive.TYPE, Primitive.PRECEDENCE); pattern.addRelationship(Primitive.TYPE, Primitive.PRECEDENCE); } index++; } precedence = false; } } Collection<Relationship> words = pattern.getRelationships(Primitive.WORD); if (words == null) { pattern.setWordCount(0); } else { pattern.setWordCount(words.size()); } return pattern; } /** * Tokenize the sentence into its words and create a vertex representation. * If the sentence was generated, then don't add prev/next. */ public Vertex createSentence(String text, boolean generated) { return createSentence(text, generated, false); } /** * Tokenize the sentence into its words and create a vertex representation. * If the sentence was generated, then don't add prev/next. */ public Vertex createSentence(String text, boolean generated, boolean reduction) { return createSentence(text, generated, false, false); } /** * Compile the template response. */ public Vertex createTemplate(String code) { Vertex formula = null; if (code.length() < MAX_TEXT) { formula = createVertex(code); if (formula.instanceOf(Primitive.FORMULA)) { return formula; } formula.addRelationship(Primitive.INSTANTIATION, Primitive.FORMULA); } TextStream stream = new TextStream(code); stream.setPosition(9); formula = SelfCompiler.getCompiler().parseTemplate(formula, stream, false, this); return formula; } /** * Compile the forumla response. */ public Vertex createFormula(String code) { Vertex formula = null; if (code.length() < MAX_TEXT) { formula = createVertex(code); if (formula.instanceOf(Primitive.FORMULA)) { return formula; } formula.addRelationship(Primitive.INSTANTIATION, Primitive.FORMULA); } TextStream stream = new TextStream(code); stream.setPosition(8); formula = new SelfByteCodeCompiler().parseFormula(formula, stream, false, this); return formula; } /** * Tokenize the sentence into its words and create a vertex representation. * If the sentence was generated, then don't add prev/next. */ public Vertex createSentence(String text, boolean generated, boolean reduction, boolean whitespace) { if (text.length() > MAX_TEXT) { return createParagraph(text); } if ((text.length() >= 10) && ("pPfFtT<".indexOf(text.charAt(0)) != -1)) { String header = text.substring(0, 8); if (header.equalsIgnoreCase("PATTERN(")) { if (text.charAt(8) != '"') { throw new SelfParseException("Pattern must start with '\"' character - " + text); } if (text.charAt(text.length() - 1) != ')') { throw new SelfParseException("Pattern must end with ')' character - " + text); } if (text.charAt(text.length() - 2) != '"') { throw new SelfParseException("Pattern must end with '\")' characters - " + text); } return createPattern(text.substring(9, text.length() - 2)); } else if (header.equalsIgnoreCase("PATTERN:")) { return createPattern(text.substring(9, text.length() - 1), new SelfByteCodeCompiler()); } else if (header.equalsIgnoreCase("FORMULA:")) { return createFormula(text); } else if (header.equalsIgnoreCase("TEMPLATE") && text.charAt(8) == '(') { return createTemplate(text); } else if (text.substring(0, 10).equalsIgnoreCase("<template>")) { Vertex formula = AIMLParser.parser().parseAIMLTemplate(text, this); if (formula != null) { return formula; } } else if ((text.substring(0, 9).equalsIgnoreCase("<pattern>") && (text.substring(text.length() - 10, text.length()).equalsIgnoreCase("</pattern>")))) { return createPattern(text.substring(9, text.length() - 10)); } } Vertex sentence = null; if (whitespace) { sentence = createVertex(); sentence.setName(text); } else { sentence = createVertex(text); if (sentence.instanceOf(Primitive.SENTENCE)) { Collection<Relationship> words = sentence.getRelationships(Primitive.WORD); if (words != null && words.size() == sentence.getWordCount()) { return sentence; } } } sentence.addRelationship(Primitive.INSTANTIATION, Primitive.SENTENCE); parseFragment(sentence, text, generated, whitespace); return sentence; } /** * Check if the sentence has been reduced, if not, then reduce. */ public void checkReduction(Vertex sentence) { if (!sentence.instanceOf(Primitive.SENTENCE) || sentence.instanceOf(Primitive.PATTERN)) { return; } String text = (String)sentence.getData(); if (!sentence.hasRelationship(Primitive.SYNONYM)) { String reduced = Utils.reduce(text); if (!text.equals(reduced) && !reduced.isEmpty()) { Vertex synonym = createSentence(reduced, true, true, false); synonym.addRelationship(Primitive.TYPE, Primitive.SYNONYM); sentence.addRelationship(Primitive.SYNONYM, synonym); } } } /** * Tokenize the fragment into its words and create a vertex representation. */ public Vertex createFragment(String text) { Vertex fragment = createVertex(text); if (!fragment.instanceOf(Primitive.FRAGMENT)) { fragment.addRelationship(Primitive.INSTANTIATION, Primitive.FRAGMENT); } Collection<Relationship> words = fragment.getRelationships(Primitive.WORD); if (words != null && words.size() == fragment.getWordCount()) { return fragment; } parseFragment(fragment, text, true, false); return fragment; } /** * Tokenize the fragment into its words and create a vertex representation. */ public void parseFragment(Vertex fragment, String text, boolean generated, boolean whitespace) { boolean learnGrammar = true; Language language = getBot().mind().getThought(Language.class); if (language != null) { learnGrammar = language.getLearnGrammar(); } TextStream stream = new TextStream(text); Vertex lastWord = null; Vertex nullValue = createVertex(Primitive.NULL); int index = 0; if (whitespace) { fragment.addRelationship(Primitive.TYPE, Primitive.SPACE); } while (!stream.atEnd()) { if (whitespace && (index > 0) && stream.skipWhitespace()) { fragment.addRelationship(Primitive.WORD, Primitive.SPACE, index); index++; } String wordText = stream.nextWord(); if (wordText == null) { break; } Vertex word = createVertex(wordText); // Check if the word is a number. if ((wordText.length() > 0) && (Character.isDigit(wordText.charAt(0)) || ((wordText.length() > 1) && ((wordText.charAt(0) == '-') || (wordText.charAt(0) == '+'))))) { try { String numeric = wordText; if (numeric.indexOf(',') != -1) { numeric = numeric.replace(",", ""); } Vertex number = null; if (wordText.indexOf('.') == -1) { BigInteger value = new BigInteger(numeric); // Create vertex associates back to the word. number = createVertex(value); } else { BigDecimal value = new BigDecimal(numeric); // Create vertex associates back to the word. number = createVertex(value); } word.addRelationship(Primitive.MEANING, number); } catch (NumberFormatException exception) { // Not a number. } // Check for URLs. } else if ((wordText.length() > 8) && wordText.substring(0, 7).equals("http://") || (wordText.length() > 9) && wordText.substring(0, 8).equals("https://")) { try { Vertex url = createVertex(new URI(wordText)); url.addRelationship(Primitive.WORD, word); word.addRelationship(Primitive.INSTANTIATION, Primitive.URL); url.addRelationship(Primitive.INSTANTIATION, Primitive.URL); url.addRelationship(Primitive.INSTANTIATION, Primitive.THING); word.addRelationship(Primitive.MEANING, url); } catch (Exception badURL) { // Ignore. } // Check for Twitter address } else if ((wordText.length() >= 2) && wordText.charAt(0) == '@') { try { word.addRelationship(Primitive.INSTANTIATION, Primitive.TWITTERADDRESS); word.addRelationship(Primitive.INSTANTIATION, Primitive.THING); word.addRelationship(Primitive.WORD, word); word.addRelationship(Primitive.MEANING, word); } catch (Exception badURL) { // Ignore. } // Check for email address } else if ((wordText.length() > 4) && (wordText.indexOf('@') != -1) && (wordText.indexOf('.') != -1)) { try { word.addRelationship(Primitive.INSTANTIATION, Primitive.EMAILADDRESS); word.addRelationship(Primitive.INSTANTIATION, Primitive.THING); word.addRelationship(Primitive.WORD, word); word.addRelationship(Primitive.MEANING, word); } catch (Exception badURL) { // Ignore. } } word.addRelationship(Primitive.INSTANTIATION, Primitive.WORD); if (!generated) { if (learnGrammar) { word.addRelationship(Primitive.SENTENCE, fragment); if (lastWord != null) { lastWord.addRelationship(Primitive.NEXT, word); word.addRelationship(Primitive.PREVIOUS, lastWord); } else { nullValue.addRelationship(Primitive.NEXT, word); word.addRelationship(Primitive.PREVIOUS, nullValue); } } } lastWord = word; fragment.addRelationship(Primitive.WORD, word, index); index++; } if (!generated && learnGrammar && (lastWord != null)) { lastWord.addRelationship(Primitive.NEXT, nullValue); } Collection<Relationship> words = fragment.getRelationships(Primitive.WORD); if (words == null) { fragment.setWordCount(0); } else { fragment.setWordCount(words.size()); } } /** * Create the word, and its meaning. * If the word or meaning exist, use the existing one. */ public synchronized Vertex createObject(String name) { Vertex word = createWord(name); // TODO: handle multiple meanings. Vertex meaning = word.mostConscious(Primitive.MEANING); if (meaning == null) { meaning = createVertex(); meaning.setName(name); log("Created meaning", Level.FINEST, word); } else { log("Existing meaning", Level.FINEST, word); return meaning; } word.addRelationship(Primitive.MEANING, meaning); meaning.addRelationship(Primitive.WORD, word); associateCaseInsensitivity(name, meaning); return meaning; } /** * Create the primitive and associate the word to it. */ public synchronized Vertex createPrimitive(String name) { Vertex object = createVertex(new Primitive(name.toLowerCase().replace(' ', '_'))); Vertex word = createWord(name); word.addRelationship(Primitive.MEANING, object); object.addRelationship(Primitive.WORD, word); associateCaseInsensitivity(name, object); return object; } /** * Create the word, and a new meaning. */ public synchronized Vertex createNewObject(String name) { Vertex meaningType = createVertex(Primitive.MEANING); Vertex wordType = createVertex(Primitive.WORD); Vertex word = createWord(name); Vertex meaning = createVertex(); meaning.setName(name); log("Created meaning", Level.FINEST, word); word.addRelationship(meaningType, meaning); meaning.addRelationship(wordType, word); associateCaseInsensitivity(name, meaning); return meaning; } /** * Find or create the speaker with the name. */ public synchronized Vertex createSpeaker(String name) { Vertex speaker = null; Vertex word = findByData(name); if (word != null) { speaker = word.mostConscious(createVertex(Primitive.MEANING), createVertex(Primitive.SPEAKER)); } if (speaker == null) { speaker = createNewObject(Utils.capitalize(name)); speaker.addRelationship(Primitive.NAME, createWord(Utils.capitalize(name))); speaker.addRelationship(Primitive.INSTANTIATION, Primitive.SPEAKER); speaker.addRelationship(Primitive.INSTANTIATION, Primitive.THING); if (name == TextEntry.DEFAULT_SPEAKER) { speaker.addRelationship(Primitive.NAME, createVertex(Utils.capitalize(name))); } associateCaseInsensitivity(name, speaker); } return speaker; } /** * Create a new anonymous speaker. */ public synchronized Vertex createAnonymousSpeaker() { String name = "anonymous"; Vertex speaker = createVertex(); Vertex word = createWord(name); speaker.addRelationship(Primitive.INSTANTIATION, Primitive.SPEAKER); speaker.addRelationship(Primitive.INSTANTIATION, Primitive.THING); speaker.addRelationship(Primitive.ASSOCIATED, Primitive.ANONYMOUS); word.addRelationship(Primitive.MEANING, speaker); speaker.addRelationship(Primitive.WORD, word); associateCaseInsensitivity(name, speaker); return speaker; } /** * Create a timestamp based on the current nanos. */ public synchronized Vertex createTimestamp() { long nanos = System.nanoTime() % 1000000000; long millis = System.currentTimeMillis(); Timestamp timestamp = new Timestamp(millis); timestamp.setNanos((int)nanos); return createVertex(timestamp); } /** * Log the message if the debug level is greater or equal to the level. */ protected void log(String message, Level level, Object... arguments) { getBot().log(this, message, level, arguments); } /** * Return the associated Bot instance. */ public Bot getBot() { return bot; } /** * Set the associated Bot instance. */ public void setBot(Bot bot) { this.bot = bot; } public boolean isShortTerm() { return isShortTerm; } public void setShortTerm(boolean isShortTerm) { this.isShortTerm = isShortTerm; } }