/** * SusiThought * Copyright 29.06.2016 by Michael Peter Christen, @0rb1t3r * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program in the file lgpl21.txt * If not, see <http://www.gnu.org/licenses/>. */ package org.loklak.susi; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.json.JSONArray; import org.json.JSONObject; /** * A thought is a piece of data that can be remembered. The structure or the thought can be * modeled as a table which may be created using the retrieval of information from elsewhere * of the current argument. */ public class SusiThought extends JSONObject { final String metadata_name, data_name; /** * create an empty thought, to be filled with single data entities. */ public SusiThought() { super(true); this.metadata_name = "metadata"; this.data_name = "data"; } /** * create a clone of a json object as a SusiThought object * @param json the 'other' thought, probably an exported and re-imported thought */ public SusiThought(JSONObject json) { this(); if (json.has(this.metadata_name)) this.put(this.metadata_name, json.getJSONObject(this.metadata_name)); if (json.has(this.data_name)) this.setData(json.getJSONArray(this.data_name)); if (json.has("actions")) this.put("actions", json.getJSONArray("actions")); } /** * Create an initial thought using the matcher on an expression. * Such an expression is like the input from a text source which contains keywords * that are essential for the thought. The matcher extracts such information. * Matching informations are named using the order of the appearance of the information pieces. * The first information is named '1', the second '2' and so on. The whole information which contained * the matching information is named '0'. * @param matcher */ public SusiThought(Matcher matcher) { this(); this.setOffset(0).setHits(1); JSONObject row = new JSONObject(); row.put("0", matcher.group(0)); for (int i = 0; i < matcher.groupCount(); i++) { row.put(Integer.toString(i + 1), matcher.group(i + 1)); } this.setData(new JSONArray().put(row)); } @Deprecated public SusiThought(String metadata_name, String data_name) { super(true); this.metadata_name = metadata_name; this.data_name = data_name; } public boolean equals(Object o) { if (!(o instanceof SusiThought)) return false; SusiThought t = (SusiThought) o; return this.getData().equals(t.getData()); } /** * In a series of information pieces the first information piece has number 0. * If the thought is a follow-up series of a previous set of information, an offset is needed. * That can be set here. * @param offset the offset to a previous set of information pieces. * @return the thought */ public SusiThought setOffset(int offset) { getMetadata().put("offset", offset); return this; } public int getOffset() { return getMetadata().has("offset") ? getMetadata().getInt("offset") : 0; } /** * The number of information pieces in a set of informations may have a count. * @return hits number of information pieces */ public int getCount() { return getData().length(); } /** * While the number of information pieces in a whole has a count, the number of relevant * information pieces may have been extracted. The hits number gives the number of relevant * pieces. This can be set here. * @param hits number of information pieces * @return the thought */ public SusiThought setHits(int hits) { getMetadata().put("hits", hits); return this; } public int getHits() { return getMetadata().has("hits") ? getMetadata().getInt("hits") : 0; } /** * The process which created this thought may have a name or description string. * To document what happened, the process namen can be given here * @param query the process which formed this thought * @return the thought */ public SusiThought setProcess(String processName) { getMetadata().put("process", processName); return this; } /** * If this thought was the result of a retrieval using a specific expression, that expression is * called the query. The query can be attached to a thought * @param query the expression which caused that this thought was formed * @return the thought */ public SusiThought setQuery(String query) { getMetadata().put("query", query); return this; } public String getQuery() { return getMetadata().has("query") ? getMetadata().getString("query") : ""; } /** * If the expression to create this thought had an agent that expressed the result set of the * information contained in this thought, it is called the scraper. The scraper name can be attached here. * @param scraperInfo the scraper that created this thought * @return the thought */ public SusiThought setScraperInfo(String scraperInfo) { getMetadata().put("scraperInfo", scraperInfo); return this; } /** * All details of the creation of this thought is collected in a metadata statement. * @return the set of meta information for this thought */ private JSONObject getMetadata() { JSONObject md; if (this.has(metadata_name)) md = this.getJSONObject(metadata_name); else { md = new JSONObject(); this.put(metadata_name, md); } if (!md.has("count")) md.put("count", getData().length()); return md; } /** * Information contained in this thought has the form of a result set table, organized in rows and columns. * The columns must have all the same name in each row. * @param table the information for this thought. * @return the thought */ public SusiThought setData(JSONArray table) { this.put(data_name, table); JSONObject md = getMetadata(); md.put("count", getData().length()); return this; } /** * Information contained in this thought can get returned as a table, a set of information pieces. * @return a table of information pieces as a set of rows which all have the same column names. */ public JSONArray getData() { if (this.has(data_name)) return this.getJSONArray(data_name); JSONArray a = new JSONArray(); this.put(data_name, a); return a; } /** * Merging of data is required during an mind-meld. * To meld two thoughts, we combine their data arrays into one. * The resulting table has the maximum length of the source tables * @param table the information to be melted into our existing table. * @return the thought */ public SusiThought mergeData(JSONArray table1) { JSONArray table0 = this.getData(); while (table0.length() < table1.length()) table0.put(new JSONObject()); for (int i = 0; i < table1.length(); i++) { table0.getJSONObject(i).putAll(table1.getJSONObject(i)); } setData(table0); return this; } /** * If during thinking we observe something that we want to memorize, we can memorize this here * @param featureName the object key * @param observation the object value * @return the thought */ public SusiThought addObservation(String featureName, String observation) { JSONArray data = getData(); for (int i = 0; i < data.length(); i++) { JSONObject spark = data.getJSONObject(i); if (!spark.has(featureName)) { spark.put(featureName, observation); return this; } } data.put(new JSONObject().put(featureName, observation)); return this; } public static final Pattern variable_pattern = Pattern.compile("\\$.*?\\$"); /** * Unification applies a piece of memory within the current argument to a statement * which creates an instantiated statement * @param statement * @return the instantiated statement with elements of the argument applied as much as possible */ public String unify(String statement) { JSONArray table = this.getData(); if (table != null && table.length() > 0) { JSONObject row = table.getJSONObject(0); for (String key: row.keySet()) { int i = statement.indexOf("$" + key + "$"); if (i >= 0) { statement = statement.substring(0, i) + row.get(key).toString() + statement.substring(i + key.length() + 2); } } } return statement; } public static void main(String[] args) { SusiThought t = new SusiThought().addObservation("a", "letter-a"); System.out.println(t.unify("the letter $a$")); } }