diff options
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.java | 285 |
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); + } + +} |