aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/prelude/semantics/RuleImporter.java
diff options
context:
space:
mode:
Diffstat (limited to 'container-search/src/main/java/com/yahoo/prelude/semantics/RuleImporter.java')
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/semantics/RuleImporter.java285
1 files changed, 285 insertions, 0 deletions
diff --git a/container-search/src/main/java/com/yahoo/prelude/semantics/RuleImporter.java b/container-search/src/main/java/com/yahoo/prelude/semantics/RuleImporter.java
new file mode 100644
index 00000000000..1dab816f22b
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/prelude/semantics/RuleImporter.java
@@ -0,0 +1,285 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.prelude.semantics;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.List;
+
+import com.yahoo.io.IOUtils;
+import com.yahoo.io.reader.NamedReader;
+import com.yahoo.prelude.semantics.parser.*;
+
+/**
+ * Imports rule bases from various sources.
+ *
+ * @author bratseth
+ */
+// Uses the JavaCC-generated parser to read rule bases.
+// This is an intermediate between the parser and the rule base being loaded
+// on implementation of some directives, for example, it knows where to find
+// rule bases included into others, while neither the rule base or the parser knows.
+public class RuleImporter {
+
+ /**
+ * If this is set, imported rule bases are looked up in this config
+ * otherwise, they are looked up as files
+ */
+ private SemanticRulesConfig config = null;
+
+ /**
+ * Ignore requests to read automata files.
+ * Useful to validate rule bases without having automatas present
+ */
+ private boolean ignoreAutomatas = false;
+
+ /**
+ * Ignore requests to include files.
+ * Useful to validate rule bases one by one in config
+ */
+ private boolean ignoreIncludes = false;
+
+ /** Create a rule importer which will read from file */
+ public RuleImporter() {
+ this(null, false);
+ }
+
+ /** Create a rule importer which will read from a config object */
+ public RuleImporter(SemanticRulesConfig config) {
+ this(config, false);
+ }
+
+ public RuleImporter(boolean ignoreAutomatas) {
+ this(null, ignoreAutomatas);
+ }
+
+ public RuleImporter(boolean ignoreAutomatas, boolean ignoreIncludes) {
+ this(null, ignoreAutomatas, ignoreIncludes);
+ }
+
+ public RuleImporter(SemanticRulesConfig config, boolean ignoreAutomatas) {
+ this.config=config;
+ this.ignoreAutomatas=ignoreAutomatas;
+ }
+
+ public RuleImporter(SemanticRulesConfig config, boolean ignoreAutomatas, boolean ignoreIncludes) {
+ this.config = config;
+ this.ignoreAutomatas = ignoreAutomatas;
+ this.ignoreIncludes = ignoreIncludes;
+ }
+
+ /**
+ * Imports semantic rules from a file
+ *
+ * @param fileName the rule file to use
+ * @throws java.io.IOException if the file can not be read for some reason
+ * @throws ParseException if the file does not contain a valid semantic rule set
+ */
+ public RuleBase importFile(String fileName) throws IOException, ParseException {
+ return importFile(fileName,null);
+ }
+
+ /**
+ * Imports semantic rules from a file
+ *
+ * @param fileName the rule file to use
+ * @param automataFile the automata file to use, or null to not use any
+ * @throws java.io.IOException if the file can not be read for some reason
+ * @throws ParseException if the file does not contain a valid semantic rule set
+ */
+ public RuleBase importFile(String fileName,String automataFile) throws IOException, ParseException {
+ return importFile(fileName,automataFile,null);
+ }
+
+ /**
+ * Imports semantic rules from a file
+ *
+ * @param fileName the rule file to use
+ * @param automataFile the automata file to use, or null to not use any
+ * @param ruleBase an existing rule base to import these rules into, or null
+ * to create a new
+ * @throws java.io.IOException if the file can not be read for some reason
+ * @throws ParseException if the file does not contain a valid semantic rule set
+ */
+ public RuleBase importFile(String fileName,String automataFile,RuleBase ruleBase) throws IOException, ParseException {
+ ruleBase=privateImportFile(fileName,automataFile,ruleBase);
+ ruleBase.initialize();
+ return ruleBase;
+ }
+
+ public RuleBase privateImportFile(String fileName,String automataFile,RuleBase ruleBase) throws IOException, ParseException {
+ BufferedReader reader=null;
+ try {
+ reader= IOUtils.createReader(fileName, "utf-8");
+ File file=new File(fileName);
+ String absoluteFileName=file.getAbsolutePath();
+ if (ruleBase==null)
+ ruleBase=new RuleBase();
+ ruleBase.setName(stripLastName(file.getName()));
+ privateImportFromReader(reader,absoluteFileName,automataFile,ruleBase);
+ return ruleBase;
+ }
+ finally {
+ IOUtils.closeReader(reader);
+ }
+ }
+
+ /** Imports all the rule files (files ending by "sr") in the given directory */
+ public List<RuleBase> importDir(String ruleBaseDir) throws IOException, ParseException {
+ File ruleBaseDirFile=new File(ruleBaseDir);
+ if (!ruleBaseDirFile.exists())
+ throw new IOException("Rule base dir '" + ruleBaseDirFile.getAbsolutePath() + "' does not exist");
+ File[] files=ruleBaseDirFile.listFiles();
+ Arrays.sort(files);
+ List<RuleBase> ruleBases=new java.util.ArrayList<>();
+ for (File file : files) {
+ if (!file.getName().endsWith(".sr")) continue;
+ RuleBase base = importFile(file.getAbsolutePath());
+ ruleBases.add(base);
+ }
+ return ruleBases;
+ }
+
+ /** Read and include a rule base in another */
+ public void include(String ruleBaseName,RuleBase ruleBase) throws java.io.IOException, ParseException {
+ if (ignoreIncludes) return;
+ RuleBase include;
+ if (config==null) {
+ include=privateImportFromDirectory(ruleBaseName,ruleBase);
+ }
+ else {
+ include=privateImportFromConfig(ruleBaseName);
+ }
+ ruleBase.include(include);
+ }
+
+ /** Returns an unitialized rule base */
+ private RuleBase privateImportFromDirectory(String ruleBaseName,RuleBase ruleBase) throws IOException, ParseException {
+ RuleBase include = new RuleBase();
+ String includeDir=new File(ruleBase.getSource()).getParentFile().getAbsolutePath();
+ if (!ruleBaseName.endsWith(".sr"))
+ ruleBaseName=ruleBaseName + ".sr";
+ File importFile=new File(includeDir,ruleBaseName);
+ if (!importFile.exists())
+ throw new IOException("No file named '" + shortenPath(importFile.getPath()) + "'");
+ return privateImportFile(importFile.getPath(),null,include);
+ }
+
+ /** Returns an unitialized rule base */
+ private RuleBase privateImportFromConfig(String ruleBaseName) throws IOException, ParseException {
+ SemanticRulesConfig.Rulebase ruleBaseConfig=findRuleBaseConfig(config,ruleBaseName);
+ if (ruleBaseConfig==null)
+ ruleBaseConfig=findRuleBaseConfig(config,stripLastName(ruleBaseName));
+ if (ruleBaseConfig==null)
+ throw new ParseException("Could not find included rule base '" + ruleBaseName + "'");
+ return privateImportConfig(ruleBaseConfig);
+ }
+
+ private SemanticRulesConfig.Rulebase findRuleBaseConfig(SemanticRulesConfig config,String ruleBaseName) {
+ for (Object aRulebase : config.rulebase()) {
+ SemanticRulesConfig.Rulebase ruleBaseConfig = (SemanticRulesConfig.Rulebase) aRulebase;
+ if (ruleBaseConfig.name().equals(ruleBaseName))
+ return ruleBaseConfig;
+ }
+ return null;
+ }
+
+ public void setAutomata(RuleBase base,String automata) {
+ if (ignoreAutomatas)
+ base.setUsesAutomata(true); // Stop it from failing on automata condition references
+ else
+ base.setAutomataFile(automata);
+ }
+
+ static String stripLastName(String fileName) {
+ int lastDotIndex=fileName.lastIndexOf(".");
+ if (lastDotIndex<0) return fileName;
+ return fileName.substring(0,lastDotIndex);
+ }
+
+ public RuleBase importString(String string, String automataFile) throws IOException, ParseException {
+ return importString(string, automataFile, null, null);
+ }
+
+ public RuleBase importString(String string, String automataFile, String sourceName) throws IOException, ParseException {
+ return importString(string, automataFile, sourceName, null);
+ }
+
+ public RuleBase importString(String string, String automataFile, RuleBase ruleBase) throws IOException, ParseException {
+ return importString(string, automataFile, null, ruleBase);
+ }
+
+ public RuleBase importString(String string, String automataFile, String sourceName, RuleBase ruleBase) throws IOException, ParseException {
+ return importFromReader(new StringReader(string), sourceName, automataFile, ruleBase);
+ }
+
+ public RuleBase importConfig(SemanticRulesConfig.Rulebase ruleBaseConfig) throws IOException, ParseException {
+ RuleBase ruleBase=privateImportConfig(ruleBaseConfig);
+ ruleBase.initialize();
+ return ruleBase;
+ }
+
+ /** Imports an unitialized rule base */
+ public RuleBase privateImportConfig(SemanticRulesConfig.Rulebase ruleBaseConfig) throws IOException, ParseException {
+ if (config==null) throw new IllegalStateException("Must initialize with config if importing from config");
+ RuleBase ruleBase = new RuleBase();
+ ruleBase.setName(ruleBaseConfig.name());
+ return privateImportFromReader(new StringReader(ruleBaseConfig.rules()),"semantic-rules.cfg",
+ ruleBaseConfig.automata(),ruleBase);
+ }
+
+ public RuleBase importFromReader(Reader reader,String sourceInfo,String automataFile) throws ParseException {
+ return importFromReader(reader,sourceInfo,automataFile,null);
+ }
+
+ /**
+ * Imports rules from a reader
+ *
+ * @param reader the reader containing rules on the proper syntax
+ * @param sourceName a string describing the source of the rules used for error messages
+ * @param ruleBase an existing rule base to import the rules into, or null to create a new one
+ * @return the rule base containing the rules added from the reader
+ * @throws ParseException if the reader contains illegal rule syntax
+ */
+ public RuleBase importFromReader(Reader reader, String sourceName, String automataFile, RuleBase ruleBase) throws ParseException {
+ ruleBase=privateImportFromReader(reader, sourceName, automataFile,ruleBase);
+ ruleBase.initialize();
+ return ruleBase;
+ }
+
+ /** Returns an unitialized rule base */
+ public RuleBase privateImportFromReader(Reader reader, String sourceName, String automataFile, RuleBase ruleBase) throws ParseException {
+ try {
+ if (ruleBase==null) {
+ ruleBase=new RuleBase();
+ if (sourceName == null)
+ sourceName = "anonymous";
+ ruleBase.setName(sourceName);
+ }
+ ruleBase.setSource(sourceName.replace('\\','/'));
+ new SemanticsParser(reader).semanticRules(ruleBase, this);
+ if (automataFile!=null && !automataFile.isEmpty())
+ ruleBase.setAutomataFile(automataFile.replace('\\','/'));
+ return ruleBase;
+ } catch (Throwable t) { // also catches token mgr errors
+ ParseException p=new ParseException("Could not parse '" + shortenPath(sourceName) + "'");
+ p.initCause(t);
+ throw p;
+ }
+ }
+
+ /**
+ * Snips what's in from of rules/ if "rules/" is present in the string
+ * to avoid displaying details about where application content is copied
+ * (if rules/ is present, these rules are read from an applicatino package)
+ */
+ private static String shortenPath(String path) {
+ int rulesIndex=path.indexOf("rules/");
+ if (rulesIndex<0) return path;
+ return path.substring(rulesIndex);
+ }
+
+}