From 5699eb248346eb6dd59f42605aeb56af41b15cab Mon Sep 17 00:00:00 2001 From: Serban Jora Date: Tue, 29 Aug 2017 15:52:57 -0400 Subject: Add initial ATT tosca checker tool Addressed license headers and copyright owner issues Addressed project folder name Issue-ID: MODELING-7 Change-Id: I150784c5871bb6093ff0a6615639088bc2e0c496 Signed-off-by: Serban Jora --- javatoscachecker/kwalify/LICENSE | 20 + javatoscachecker/kwalify/pom.xml | 104 +++ .../src/main/java/kwalify/BaseException.java | 51 ++ .../main/java/kwalify/CommandOptionException.java | 33 + .../kwalify/src/main/java/kwalify/Defaultable.java | 18 + .../src/main/java/kwalify/DefaultableHashMap.java | 48 ++ .../main/java/kwalify/InvalidPathException.java | 23 + .../main/java/kwalify/InvalidTypeException.java | 21 + .../src/main/java/kwalify/KwalifyException.java | 20 + .../main/java/kwalify/KwalifyRuntimeException.java | 19 + .../kwalify/src/main/java/kwalify/Main.java | 305 ++++++++ .../kwalify/src/main/java/kwalify/Messages.java | 51 ++ .../src/main/java/kwalify/MetaValidator.java | 475 +++++++++++ .../kwalify/src/main/java/kwalify/Parser.java | 19 + .../src/main/java/kwalify/PlainYamlParser.java | 870 +++++++++++++++++++++ .../kwalify/src/main/java/kwalify/Rule.java | 673 ++++++++++++++++ .../src/main/java/kwalify/SchemaException.java | 22 + .../src/main/java/kwalify/SyntaxException.java | 28 + .../kwalify/src/main/java/kwalify/Types.java | 107 +++ .../kwalify/src/main/java/kwalify/Util.java | 646 +++++++++++++++ .../src/main/java/kwalify/ValidationException.java | 26 + .../kwalify/src/main/java/kwalify/Validator.java | 415 ++++++++++ .../kwalify/src/main/java/kwalify/YamlParser.java | 156 ++++ .../src/main/java/kwalify/YamlSyntaxException.java | 23 + .../kwalify/src/main/java/kwalify/YamlUtil.java | 62 ++ .../src/main/java/kwalify/messages.properties | 110 +++ .../src/main/resources/kwalify/messages.properties | 110 +++ 27 files changed, 4455 insertions(+) create mode 100644 javatoscachecker/kwalify/LICENSE create mode 100644 javatoscachecker/kwalify/pom.xml create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/BaseException.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/CommandOptionException.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/Defaultable.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/DefaultableHashMap.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/InvalidPathException.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/InvalidTypeException.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/KwalifyException.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/KwalifyRuntimeException.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/Main.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/Messages.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/MetaValidator.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/Parser.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/PlainYamlParser.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/Rule.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/SchemaException.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/SyntaxException.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/Types.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/Util.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/ValidationException.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/Validator.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/YamlParser.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/YamlSyntaxException.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/YamlUtil.java create mode 100644 javatoscachecker/kwalify/src/main/java/kwalify/messages.properties create mode 100644 javatoscachecker/kwalify/src/main/resources/kwalify/messages.properties (limited to 'javatoscachecker/kwalify') diff --git a/javatoscachecker/kwalify/LICENSE b/javatoscachecker/kwalify/LICENSE new file mode 100644 index 0000000..369d782 --- /dev/null +++ b/javatoscachecker/kwalify/LICENSE @@ -0,0 +1,20 @@ +copyright(c) 2005 kuwata-lab all rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/javatoscachecker/kwalify/pom.xml b/javatoscachecker/kwalify/pom.xml new file mode 100644 index 0000000..7acfa80 --- /dev/null +++ b/javatoscachecker/kwalify/pom.xml @@ -0,0 +1,104 @@ + + 4.0.0 + + + org.onap.tosca + checker + 0.0.1-SNAPSHOT + + kwalify + jar + kwalify + + + src/main/java + + + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + copy-dependencies + package + + copy-dependencies + + + ${project.build.directory}/deps + false + false + true + + + + + + org.codehaus.mojo + buildnumber-maven-plugin + 1.4 + + + validate + + create + + + + + false + false + + + + org.apache.maven.plugins + maven-jar-plugin + 2.1 + + + + true + + + ${buildNumber} + + + + + + + + com.blackducksoftware.integration + hub-maven-plugin + 2.0.0 + false + + ${project.name} + ${project.basedir} + false + + + + create-bdio-file + package + + build-bom + + + + + + + + + + diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/BaseException.java b/javatoscachecker/kwalify/src/main/java/kwalify/BaseException.java new file mode 100644 index 0000000..c2cc83b --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/BaseException.java @@ -0,0 +1,51 @@ +/* + * @(#)BaseException.java $Rev: 3 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +/** + * base class of ValidationException and SchemaException. + * + * @revision $Rev: 3 $ + * @release $Release: 0.5.1 $ + */ +public abstract class BaseException extends KwalifyRuntimeException implements Comparable { + + String _ypath; + Object _value; + Rule _rule; + String _errorSymbol; + int _linenum = -1; + + public BaseException(String message, String ypath, Object value, Rule rule, String errorSymbol) { + super(message); + _ypath = ypath; + _value = value; + _rule = rule; + _errorSymbol = errorSymbol; + } + + public String getPath() { return _ypath.equals("") ? "/" : _ypath; } + //public void setPath(String ypath) { _ypath = ypath; } + + public Object getValue() { return _value; } + //public void setValue(Object value) { _value = value; } + + public Rule getRule() { return _rule; } + // + //public void setRule(Rule rule) { _rule = rule; } + + public String getErrorSymbol() { return _errorSymbol; } + //public void setErrorSymbol(String errorSymbol) { _errorSymbol = errorSymbol; } + + public int getLineNumber() { return _linenum; } + public void setLineNumber(int linenum) { _linenum = linenum; } + + public int compareTo(Object obj) { + int n = ((ValidationException)obj).getLineNumber(); + return _linenum - n; + } +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/CommandOptionException.java b/javatoscachecker/kwalify/src/main/java/kwalify/CommandOptionException.java new file mode 100644 index 0000000..e35be85 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/CommandOptionException.java @@ -0,0 +1,33 @@ +/* + * @(#)CommandOptionException.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +/** + * exception class thrown if command-line option is wrong + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + */ +public class CommandOptionException extends KwalifyException { + private static final long serialVersionUID = 6433387612335104714L; + + private String _error_symbol = null; + private char _option; + + public CommandOptionException(String message, char option, String error_symbol) { + super(message); + _option = option; + _error_symbol = error_symbol; + } + + public String getErrorSymbol() { return _error_symbol; } + public void setErrorSymbol(String error_symbol) { _error_symbol = error_symbol; } + + public char getOption() { return _option; } + public void setOption(char option) { _option = option; } + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/Defaultable.java b/javatoscachecker/kwalify/src/main/java/kwalify/Defaultable.java new file mode 100644 index 0000000..7e7c692 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/Defaultable.java @@ -0,0 +1,18 @@ +/* + * @(#)Defaultable.java $Rev: 3 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +/** + * interface to have default value + * + * @revision $Rev: 3 $ + * @release $Release: 0.5.1 $ + */ +public interface Defaultable { + public Object getDefault(); + public void setDefault(Object value); +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/DefaultableHashMap.java b/javatoscachecker/kwalify/src/main/java/kwalify/DefaultableHashMap.java new file mode 100644 index 0000000..0009205 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/DefaultableHashMap.java @@ -0,0 +1,48 @@ +/* + * @(#)DefaultableHashMap.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +import java.util.Map; +import java.util.HashMap; + +/** + * hash map which can have default value + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + */ +public class DefaultableHashMap extends HashMap implements Defaultable { + + private static final long serialVersionUID = -5224819562023897380L; + + private Object _default = null; + + public DefaultableHashMap() { + super(); + } + + public DefaultableHashMap(int initialCapacity) { + super(initialCapacity); + } + + public DefaultableHashMap(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + } + + public DefaultableHashMap(Map m) { + super(m); + } + + public Object getDefault() { return _default; } + + public void setDefault(Object value) { _default = value; } + + public Object get(Object key) { + return containsKey(key) ? super.get(key) : _default; + } + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/InvalidPathException.java b/javatoscachecker/kwalify/src/main/java/kwalify/InvalidPathException.java new file mode 100644 index 0000000..94eeca2 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/InvalidPathException.java @@ -0,0 +1,23 @@ +/* + * @(#)InvalidPathException.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +/** + * exception class thrown by YamlParser#setErrorsLineNumber() when path is wrong + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + */ +public class InvalidPathException extends KwalifyRuntimeException { + private static final long serialVersionUID = -4601461998104850880L; + + //private int _linenum; + + public InvalidPathException(String message) { + super(message); + } +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/InvalidTypeException.java b/javatoscachecker/kwalify/src/main/java/kwalify/InvalidTypeException.java new file mode 100644 index 0000000..fe60ca0 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/InvalidTypeException.java @@ -0,0 +1,21 @@ +/* + * @(#)InvalidTypeException.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +/** + * exception class thrown by Util.compareValues() when comparing different type values. + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + */ +public class InvalidTypeException extends KwalifyRuntimeException { + private static final long serialVersionUID = -6937887618526171845L; + + public InvalidTypeException(String message) { + super(message); + } +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/KwalifyException.java b/javatoscachecker/kwalify/src/main/java/kwalify/KwalifyException.java new file mode 100644 index 0000000..976a263 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/KwalifyException.java @@ -0,0 +1,20 @@ +/* + * @(#)KwalifyException.java $Rev: 3 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +/** + * base exception class of all exception in Kwalify + * + * @revision $Rev: 3 $ + * @release $Release: 0.5.1 $ + * @see KwalifyRuntimeException + */ +public abstract class KwalifyException extends Exception { + public KwalifyException(String message) { + super(message); + } +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/KwalifyRuntimeException.java b/javatoscachecker/kwalify/src/main/java/kwalify/KwalifyRuntimeException.java new file mode 100644 index 0000000..75e4764 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/KwalifyRuntimeException.java @@ -0,0 +1,19 @@ +/* + * @(#)KwalifyRuntimeException.java $Rev: 3 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +/** + * base class of all runtime exception class in Kwalify + * + * @revision $Rev: 3 $ + * @release $Release: 0.5.1 $ + */ +public abstract class KwalifyRuntimeException extends RuntimeException { + public KwalifyRuntimeException(String message) { + super(message); + } +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/Main.java b/javatoscachecker/kwalify/src/main/java/kwalify/Main.java new file mode 100644 index 0000000..d2c39e2 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/Main.java @@ -0,0 +1,305 @@ +/* + * @(#)Main.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Collections; +import java.util.regex.Matcher; +import java.io.IOException; + +/** + * class for main program + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + */ +public class Main { + + private String _command; + private boolean _flag_help = false; // print help + private boolean _flag_version = false; // print version + private boolean _flag_silent = false; // suppress messages + private boolean _flag_meta = false; // meta validation + private boolean _flag_untabify = false; // expand tab charactor to spaces + private boolean _flag_emacs = false; // show errors in emacs style + private boolean _flag_linenum = false; // show line number on where errors happened + private boolean _flag_debug = false; // internal use only + private String _schema_filename = null; // schema filename + private Map _properties = new HashMap(); + + + boolean isDebug() { return _flag_debug; } + + + public String inspect() { + StringBuffer sb = new StringBuffer(); + sb.append("command : ").append(_command ).append('\n'); + sb.append("flag_help : ").append(_flag_help ).append('\n'); + sb.append("flag_version : ").append(_flag_version ).append('\n'); + sb.append("flag_silent : ").append(_flag_silent ).append('\n'); + sb.append("flag_meta : ").append(_flag_meta ).append('\n'); + sb.append("flag_untabify : ").append(_flag_untabify ).append('\n'); + sb.append("flag_emacs : ").append(_flag_emacs ).append('\n'); + sb.append("flag_linenum : ").append(_flag_linenum ).append('\n'); + sb.append("flag_debug : ").append(_flag_debug ).append('\n'); + sb.append("schema_filename : ").append(_schema_filename).append('\n'); + sb.append("properties:\n"); + for (Iterator it = _properties.keySet().iterator(); it.hasNext(); ) { + Object key = it.next(); + Object val = _properties.get(key); + sb.append(" ").append(key).append(": ").append(val).append('\n'); + } + return sb.toString(); + } + + + private static final String REVISION = "$Release: 0.5.1 $"; + private static final String HELP = "" + + "Usage1: %s [-hvstlE] -f schema.yaml doc.yaml [doc2.yaml ...]\n" + + "Usage2: %s [-hvstlE] -m schema.yaml [schema2.yaml ...]\n" + + " -h, --help : help\n" + + " -v : version\n" + + " -s : silent\n" + + " -f schema.yaml : schema definition file\n" + + " -m : meta-validation mode\n" + + " -t : expand tab character automatically\n" + + " -l : show linenumber when errored (experimental)\n" + + " -E : show errors in emacs-style (implies '-l')\n" + ; + + + public Main(String command) { + _command = command; + } + + + public String execute(String[] args) throws IOException, CommandOptionException, SyntaxException { + // parse command-line options + String[] filenames = parseOptions(args); + + // help or version + StringBuffer sb = new StringBuffer(); + if (_flag_version) { + sb.append(version()).append('\n'); + } + if (_flag_help) { + sb.append(help()); + } + if (sb.length() > 0) { + return sb.toString(); + } + + // main + String s = null; + if (_flag_meta) { + s = metaValidate(filenames); + } else if (_schema_filename == null) { + throw optionError("command.option.noaction", '\0'); + } else if (_flag_debug) { + s = inspectSchemaFile(_schema_filename); + } else { + s = validate(filenames, _schema_filename); + } + + // + return s; + } + + + private String[] parseOptions(String[] args) throws CommandOptionException { + Object[] ret = null; + try { + ret = Util.parseCommandOptions(args, "hvsmtlED", "f", null); + } catch (CommandOptionException ex) { + String error_symbol = ex.getErrorSymbol(); + if (error_symbol.equals("command.option.noarg")) { + switch (ex.getOption()) { + case 'f': error_symbol = "command.option.noschema"; break; + default: + assert false; + } + } + throw optionError(error_symbol, ex.getOption()); + } + // + Map options = (Map)ret[0]; + Map properties = (Map)ret[1]; + String[] filenames = (String[])ret[2]; + // + _flag_help = options.get("h") != null; + _flag_version = options.get("v") != null; + _flag_silent = options.get("s") != null; + _flag_meta = options.get("m") != null; + _flag_untabify = options.get("t") != null; + _flag_emacs = options.get("E") != null; + _flag_linenum = options.get("l") != null || _flag_emacs; + _flag_debug = options.get("D") != null; + _schema_filename = (String)options.get("f"); + // + // + _properties = properties; + if (_properties.get("help") != null) { + _flag_help = true; + } + // + return filenames; + } + + + private String validate(String[] filenames, String schema_filename) throws IOException, SyntaxException { + String str = Util.readFile(schema_filename); + if (_flag_untabify) { + str = Util.untabify(str); + } + YamlParser parser = new YamlParser(str); + Object schema = parser.parse(); + Validator validator = new Validator(schema); + String s = validateFiles(validator, filenames); + return s; + } + + + private String validateFiles(Validator validator, String[] filenames) throws IOException, SyntaxException { + if (filenames.length == 0) { + filenames = new String[] { null }; + } + StringBuffer sb = new StringBuffer(); + for (int j = 0; j < filenames.length; j++) { + String filename = filenames[j]; + String str = null; + if (filename == null) { + str = Util.readInputStream(System.in); + filename = "(stdin)"; + } else { + str = Util.readFile(filename); + } + if (_flag_untabify) { + str = Util.untabify(str); + } + YamlParser parser = new YamlParser(str); + int i = 0; + while (parser.hasNext()) { + Object doc = parser.parse(); + validateDocument(sb, validator, doc, filename, i, parser); + i++; + } + } + return sb.toString(); + } + + + private void validateDocument(StringBuffer sb, Validator validator, Object doc, String filename, int i, YamlParser parser) { + if (doc == null) { + Object[] args = { filename, new Integer(i) }; + String msg = Messages.buildMessage("validation.empty", null, args); + sb.append(msg).append('\n'); + return; + } + List errors = validator.validate(doc); + Object[] args = { filename, new Integer(i) }; + if (errors == null || errors.size() == 0) { + if (! _flag_silent) { + String msg = Messages.buildMessage("validation.valid", args); + sb.append(msg).append('\n'); + } + } else { + String msg = Messages.buildMessage("validation.invalid", args); + sb.append(msg).append('\n'); + if (_flag_linenum) { + assert parser != null; + parser.setErrorsLineNumber(errors); + Collections.sort(errors); + } + for (Iterator it = errors.iterator(); it.hasNext(); ) { + ValidationException error = (ValidationException)it.next(); + if (_flag_emacs) { + assert _flag_linenum; + sb.append(filename).append(":").append(error.getLineNumber()).append(":"); + } else if (_flag_linenum) { + sb.append(" - (line ").append(error.getLineNumber()).append(")"); + } else { + sb.append(" -"); + } + sb.append(" [").append(error.getPath()).append("] ").append(error.getMessage()).append('\n'); + } + } + } + + + private String metaValidate(String[] filenames) throws IOException, SyntaxException { + Validator meta_validator = MetaValidator.instance(); + String s = validateFiles(meta_validator, filenames); + return s; + } + + + private String inspectSchemaFile(String schema_filename) throws IOException, SyntaxException { + String filename = schema_filename; + String content = filename != null ? Util.readFile(filename) : Util.readInputStream(System.in); + YamlParser parser = new YamlParser(content); + Object schema = parser.parse(); + if (schema == null) { + return null; + } + Validator validator = new Validator(schema); // SchemaException is thrown when schema is wrong + String s = validator.getRule().inspect(); + if (s.charAt(s.length() - 1) != '\n') { + s = s + '\n'; + } + return s; + } + + + private static CommandOptionException optionError(String error_symbol, char option) { + Object[] args = { Character.toString(option) }; + String message = Messages.buildMessage(error_symbol, null, args); + return new CommandOptionException(message, option, error_symbol); + } + + + private String version() { + Matcher m = Util.matcher(REVISION, "[.\\d]+"); + m.find(); + String version = m.group(0); + return version; + } + + + private String help() { + String help_msg = Messages.buildMessage("command.help", null, new Object[] { _command, _command }); + //String help = HELP.replaceAll("%s", _command); + return help_msg; + } + + + public static void main(String[] args) throws Exception { + int status = 0; + Main main = null; + try { + main = new Main("kwalify-java"); + String result = main.execute(args); + if (result != null) { + System.out.println(result); + } + } catch (Exception ex) { + if (main != null && main.isDebug()) { + throw ex; + } + if ( ex instanceof CommandOptionException + || ex instanceof SyntaxException + || ex instanceof IOException) { + System.err.println("ERROR: " + ex.getMessage()); + status = 1; + } + } + System.exit(status); + } + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/Messages.java b/javatoscachecker/kwalify/src/main/java/kwalify/Messages.java new file mode 100644 index 0000000..b77f04b --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/Messages.java @@ -0,0 +1,51 @@ +/* + * @(#)Messages.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +import java.util.ResourceBundle; +//import java.util.Locale; + +/** + * set of utility methods around messages. + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + */ +public class Messages { + + private static final String __basename = "kwalify.messages"; + private static ResourceBundle __messages = ResourceBundle.getBundle(__basename); + //private static ResourceBundle __messages = ResourceBundle.getBundle(__basename, Locale.getDefault()); + + public static String message(String key) { + return __messages.getString(key); + } + + public static String buildMessage(String key, Object[] args) { + return buildMessage(key, null, args); + } + + public static String buildMessage(String key, Object value, Object[] args) { + String msg = message(key); + assert msg != null; + if (args != null) { + for (int i = 0; i < args.length; i++) { // don't use MessageFormat + msg = msg.replaceFirst("%[sd]", escape(args[i])); + } + } + if (value != null && !Types.isCollection(value)) { + msg = "'" + escape(value) + "': " + msg; + } + return msg; + } + + private static String escape(Object obj) { + //return obj.toString().replaceAll("\\", "\\\\").replace("\n", "\\n"); // J2SK1.4 doesn't support String#replace(CharSequence, CharSequence)! + return obj.toString().replaceAll("\\\\", "\\\\\\\\").replaceAll("\\n", "\\\\n"); + } + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/MetaValidator.java b/javatoscachecker/kwalify/src/main/java/kwalify/MetaValidator.java new file mode 100644 index 0000000..9ce05bd --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/MetaValidator.java @@ -0,0 +1,475 @@ +/* + * @(#)MetaValidator.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +import java.util.Map; +import java.util.List; +import java.util.Iterator; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.regex.PatternSyntaxException; + +/** + * meta validator to validate schema definition + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + */ +public class MetaValidator extends Validator { + + public static final String META_SCHEMA = "" + + "name: MAIN\n" + + "type: map\n" + + "required: yes\n" + + "mapping: &main-rule\n" + + " \"name\":\n" + + " type: str\n" + + " \"desc\":\n" + + " type: str\n" + + " \"type\":\n" + + " type: str\n" + + " #required: yes\n" + + " enum:\n" + + " - seq\n" + + " #- sequence\n" + + " #- list\n" + + " - map\n" + + " #- mapping\n" + + " #- hash\n" + + " - str\n" + + " #- string\n" + + " - int\n" + + " #- integer\n" + + " - float\n" + + " - number\n" + + " #- numeric\n" + + " - bool\n" + + " #- boolean\n" + + " - text\n" + + " - date\n" + + " - time\n" + + " - timestamp\n" + + " #- object\n" + + " - any\n" + + " - scalar\n" + + " #- collection\n" + + " \"required\":\n" + + " type: bool\n" + + " \"enum\":\n" + + " type: seq\n" + + " sequence:\n" + + " - type: scalar\n" + + " unique: yes\n" + + " \"pattern\":\n" + + " type: str\n" + + " \"assert\":\n" + + " type: str\n" + + " pattern: /\\bval\\b/\n" + + " \"range\":\n" + + " type: map\n" + + " mapping:\n" + + " \"max\":\n" + + " type: scalar\n" + + " \"min\":\n" + + " type: scalar\n" + + " \"max-ex\":\n" + + " type: scalar\n" + + " \"min-ex\":\n" + + " type: scalar\n" + + " \"length\":\n" + + " type: map\n" + + " mapping:\n" + + " \"max\":\n" + + " type: int\n" + + " \"min\":\n" + + " type: int\n" + + " \"max-ex\":\n" + + " type: int\n" + + " \"min-ex\":\n" + + " type: int\n" + + " \"ident\":\n" + + " type: bool\n" + + " \"unique\":\n" + + " type: bool\n" + + " \"sequence\":\n" + + " name: SEQUENCE\n" + + " type: seq\n" + + " sequence:\n" + + " - type: map\n" + + " mapping: *main-rule\n" + + " name: MAIN\n" + + " #required: yes\n" + + " \"mapping\":\n" + + " name: MAPPING\n" + + " type: map\n" + + " mapping:\n" + + " =:\n" + + " type: map\n" + + " mapping: *main-rule\n" + + " name: MAIN\n" + + " #required: yes\n" + ; + + + /** + * + * ex. + *
+     *  MetaValidator meta_validator = MetaValidator();
+     *  Map schema = YamlUtil.loadFile("schema.yaml");
+     *  List errors = meta_validator.validate(schema);
+     *  if (errors != null && errors.size() > 0) {
+     *    for (Iterator it = errors.iterator(); it.hasNext(); ) {
+     *      ValidationException error = (ValidationException)it.next();
+     *      System.err.println(" - [" + error.getPath() + "] " + error.getMessage());
+     *    }
+     *  }
+     * 
+ */ + + private static Validator __instance; + + public static Validator instance() { + // should not use double checked pattern? + // but it would work well because __instance is read-only. + if (__instance == null) { + synchronized (MetaValidator.class) { + if (__instance == null) { + try { + Map schema = (Map)YamlUtil.load(META_SCHEMA); + __instance = new MetaValidator(schema); + } catch (SyntaxException ex) { + assert false; + } + } + } + } + return __instance; + } + + private MetaValidator(Map schema) { + super(schema); + } + + public void postValidationHook(Object value, Rule rule, ValidationContext theContext) { + if (value == null) { + return; // realy? + } + if (! "MAIN".equals(rule.getName())) { + return; + } + // + assert value instanceof Map; + Map map = (Map)value; + String type = (String)map.get("type"); + if (type == null) { + type = Types.getDefaultType(); + } + //Class type_class = Types.typeClass(type); + //if (type_class == null) { + // theContext.addError(validationError("type.unknown", rule, path + "/type", type, null)); + //} + // + //String pattern; + //if ((pattern = (String)map.get("pattern")) != null) { + if (map.containsKey("pattern")) { + String pattern = (String)map.get("pattern"); + Matcher m = Util.matcher(pattern, "\\A\\/(.*)\\/([mi]?[mi]?)\\z"); + String pat = m.find() ? m.group(1) : pattern; + try { + Pattern.compile(pat); + } catch (PatternSyntaxException ex) { + theContext.addError("pattern.syntaxerr", rule, "pattern", pattern, null); + } + } + // + //List enum_list; + //if ((enum_list = (List)map.get("enum")) != null) { + if (map.containsKey("enum")) { + List enum_list = (List)map.get("enum"); + if (Types.isCollectionType(type)) { + theContext.addError("enum.notscalar", rule, "enum:", (Object[])null); + } else { + for (Iterator it = enum_list.iterator(); it.hasNext(); ) { + Object elem = it.next(); + if (! Types.isCorrectType(elem, type)) { + theContext.addError("enum.type.unmatch", rule, "enum", elem, new Object[] { Types.typeName(type) }); + } + } + } + } + // + //String assert_str; + //if ((assert_str = (String)map.get("assert")) != null) { + if (map.containsKey("assert")) { + System.err.println("*** warning: sorry, 'assert:' is not supported in current version of Kwalify-java."); + //String assert_str = (String)map.get("assert"); + //if (! Util.matches(assert_str, "\\bval\\b")) { + // theContext.addError(validationError("assert.noval", rule, path + "/assert", assert_str, null); + //} + //try { + // Expression.parse(assert_str); + //} catch (InvalidExpressionException ex) { + // theContext.addError(validationError("assert.syntaxerr", rule, path + "/assert", assert_str, null)); + //} + } + // + //Map range; + //if ((range = (Map)map.get("range")) != null) { + if (map.containsKey("range")) { + Map range = (Map)map.get("range"); + //if (! (range instanceof Map)) { + // theContext.addError(validtionError("range.notmap", rule, path + "/range", range, null)); + //} else + if (Types.isCollectionType(type) || type.equals("bool") || type.equals("any")) { + theContext.addError("range.notscalar", rule, "range:", null, null); + } else { + for (Iterator it = range.keySet().iterator(); it.hasNext(); ) { + String k = (String)it.next(); + Object v = range.get(k); + if (! Types.isCorrectType(v, type)) { + theContext.addError("range.type.unmatch", rule, "range/" + k, v, new Object[] { Types.typeName(type) }); + } + } + } + if (range.containsKey("max") && range.containsKey("max-ex")) { + theContext.addError("range.twomax", rule, "range", null, null); + } + if (range.containsKey("min") && range.containsKey("min-ex")) { + theContext.addError("range.twomin", rule, "range", null, null); + } + Object max = range.get("max"); + Object min = range.get("min"); + Object max_ex = range.get("max-ex"); + Object min_ex = range.get("min-ex"); + Object[] args = null; + //String error_symbol = null; + if (max != null) { + if (min != null && Util.compareValues(max, min) < 0) { + args = new Object[] { max, min }; + theContext.addError("range.maxltmin", rule, "range", null, args); + } else if (min_ex != null && Util.compareValues(max, min_ex) <= 0) { + args = new Object[] { max, min_ex }; + theContext.addError("range.maxleminex", rule, "range", null, args); + } + } else if (max_ex != null) { + if (min != null && Util.compareValues(max_ex, min) <= 0) { + args = new Object[] { max_ex, min }; + theContext.addError("range.maxexlemin", rule, "range", null, args); + } else if (min_ex != null && Util.compareValues(max_ex, min_ex) <= 0) { + args = new Object[] { max_ex, min_ex }; + theContext.addError("range.maxexleminex", rule, "range", null, args); + } + } + } + // + //Map length; + //if ((length = (Map)map.get("length")) != null) { + if (map.containsKey("length")) { + Map length = (Map)map.get("length"); + //if (! (length instanceof Map)) { + // theContext.addError(validtionError("length.notmap", rule, path + "/length", length, null)); + //} else + if (! (type.equals("str") || type.equals("text"))) { + theContext.addError("length.nottext", rule, "length:", (Object[])null); + } + //for (Iterator it = length.keySet().iterator(); it.hasNext(); ) { + // String k = (String)it.next(); + // Object v = length.get(k); + // if (k == null || ! (k.equals("max") || k.equals("min") || k.equals("max-ex") || k.equals("min-ex"))) { + // theContext.addError(validationError("length.undefined", rule, path + "/length/" + k, "" + k + ":", null)); + // } else if (! (v instanceof Integer)) { + // theContext.addError(validationError("length.notint", rule, path + "/length/" + k, v, null)); + // } + //} + if (length.containsKey("max") && length.containsKey("max-ex")) { + theContext.addError("length.twomax", rule, "length", (Object[])null); + } + if (length.containsKey("min") && length.containsKey("min-ex")) { + theContext.addError("length.twomin", rule, "length", (Object[])null); + } + Integer max = (Integer)length.get("max"); + Integer min = (Integer)length.get("min"); + Integer max_ex = (Integer)length.get("max-ex"); + Integer min_ex = (Integer)length.get("min-ex"); + Object[] args = null; + //String error_symbol = null; + if (max != null) { + if (min != null && max.compareTo(min) < 0) { + args = new Object[] { max, min }; + theContext.addError("length.maxltmin", rule, "length", null, args); + } else if (min_ex != null && max.compareTo(min_ex) <= 0) { + args = new Object[] { max, min_ex }; + theContext.addError("length.maxleminex", rule, "length", null, args); + } + } else if (max_ex != null) { + if (min != null && max_ex.compareTo(min) <= 0) { + args = new Object[] { max_ex, min }; + theContext.addError("length.maxexlemin", rule, "length", null, args); + } else if (min_ex != null && max_ex.compareTo(min_ex) <= 0) { + args = new Object[] { max_ex, min_ex }; + theContext.addError("length.maxexleminex", rule, "length", null, args); + } + } + } + // + //Boolean unique; + //if ((unique = (Boolean)map.get("unique")) != null) { + if (map.containsKey("unique")) { + Boolean unique = (Boolean)map.get("unique"); + if (unique.booleanValue() == true && Types.isCollectionType(type)) { + theContext.addError("unique.notscalar", rule, "unique:", (Object[])null); + } + if (theContext.getPath().length() == 0) { + theContext.addError("unique.onroot", rule, "", "unique:", null); + } + } + // + //Boolean ident; + //if ((ident = (Boolean)map.get("ident")) != null) { + if (map.containsKey("ident")) { + Boolean ident = (Boolean)map.get("ident"); + if (ident.booleanValue() == true && Types.isCollectionType(type)) { + theContext.addError("ident.notscalar", rule, "ident:", (Object[])null); + } + if (theContext.getPath().length() == 0) { + theContext.addError("ident.onroot", rule, "/", "ident:", (Object[])null); + } + } + // + //List seq; + //if ((seq = (List)map.get("sequence")) != null) { + if (map.containsKey("sequence")) { + List seq = (List)map.get("sequence"); + //if (! (seq instanceof List)) { + // theContext.addError(validationError("sequence.notseq", rule, path + "/sequence", seq, null)); + //} else + if (seq == null || seq.size() == 0) { + theContext.addError("sequence.noelem", rule, "sequence", seq, null); + } else if (seq.size() > 1) { + theContext.addError("sequence.toomany", rule, "sequence", seq, null); + } else { + Object item = seq.get(0); + assert item instanceof Map; + Map m = (Map)item; + Boolean ident2 = (Boolean)m.get("ident"); + if (ident2 != null && ident2.booleanValue() == true && ! "map".equals(m.get("type"))) { + theContext.addError("ident.notmap", null, "sequence/0", "ident:", null); + } + } + } + // + //Map mapping; + //if ((mapping = (Map)map.get("mapping")) != null) { + if (map.containsKey("mapping")) { + Map mapping = (Map)map.get("mapping"); + //if (mapping != null && ! (mapping instanceof Map)) { + // theContext.addError(validationError("mapping.notmap", rule, path + "/mapping", mapping, null)); + //} else + Object default_value = null; + if (mapping != null && mapping instanceof Defaultable) { + default_value = ((Defaultable)mapping).getDefault(); + } + if (mapping == null || (mapping.size() == 0 && default_value == null)) { + theContext.addError("mapping.noelem", rule, "mapping", mapping, null); + } + } + // + if (type.equals("seq")) { + if (! map.containsKey("sequence")) { + theContext.addError("seq.nosequence", rule, null, (Object[])null); + } + //if (map.containsKey("enum")) { + // theContext.addError(validationError("seq.conflict", rule, path, "enum:", null)); + //} + if (map.containsKey("pattern")) { + theContext.addError("seq.conflict", rule, "pattern:", (Object[])null); + } + if (map.containsKey("mapping")) { + theContext.addError("seq.conflict", rule, "mapping:", (Object[])null); + } + //if (map.containsKey("range")) { + // theContext.addError(validationError("seq.conflict", rule, path, "range:", null)); + //} + //if (map.containsKey("length")) { + // theContext.addError(validationError("seq.conflict", rule, path, "length:", null)); + //} + } else if (type.equals("map")) { + if (! map.containsKey("mapping")) { + theContext.addError("map.nomapping", rule, null, (Object[])null); + } + //if (map.containsKey("enum")) { + // theContext.addError(validationError("map.conflict", rule, path, "enum:", null)); + //} + if (map.containsKey("pattern")) { + theContext.addError("map.conflict", rule, "pattern:", (Object[])null); + } + if (map.containsKey("sequence")) { + theContext.addError("map.conflict", rule, "sequence:", (Object[])null); + } + //if (map.containsKey("range")) { + // theContext.addError(validationError("map.conflict", rule, path, "range:", null)); + //} + //if (map.containsKey("length")) { + // theContext.addError(validationError("map.conflict", rule, path, "length:", null)); + //} + } else { + if (map.containsKey("sequence")) { + theContext.addError("scalar.conflict", rule, "sequence:", (Object[])null); + } + if (map.containsKey("mapping")) { + theContext.addError("scalar.conflict", rule, "mapping:", (Object[])null); + } + if (map.containsKey("enum")) { + if (map.containsKey("range")) { + theContext.addError("enum.conflict", rule, "range:", (Object[])null); + } + if (map.containsKey("length")) { + theContext.addError("enum.conflict", rule, "length:", (Object[])null); + } + if (map.containsKey("pattern")) { + theContext.addError("enum.conflict", rule, "pattern:", (Object[])null); + } + } + } + } + +/* + public static void main(String[] args) { + try { + // parse schema + String filename = args.length > 0 ? args[0] : "schema.yaml"; + String schema_str = Util.readFile(filename); + YamlParser parser = new YamlParser(schema_str); + Object schema = parser.parse(); + + // validate schema + Validator meta_validator = MetaValidator.instance(); + List errors = meta_validator.validate(schema); + + // show errors + if (errors != null && errors.size() > 0) { + parser.setErrorsLineNumber(errors); + for (Iterator it = errors.iterator(); it.hasNext(); ) { + ValidationException error = (ValidationException)it.next(); + int linenum = error.getLineNumber(); + String path = error.getPath(); + String msg = error.getMessage(); + System.out.println("- line " + linenum + ": [" + path + "] " + msg); + } + } else { + System.out.println("meta validation: OK."); + } + } catch (SyntaxException ex) { + ex.printStackTrace(); + } catch (java.io.IOException ex) { + ex.printStackTrace(); + } + } +*/ + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/Parser.java b/javatoscachecker/kwalify/src/main/java/kwalify/Parser.java new file mode 100644 index 0000000..53c6272 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/Parser.java @@ -0,0 +1,19 @@ +/* + * @(#)Parser.java $Rev: 3 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +/** + * interface for any parser + * + * @revision $Rev: 3 $ + * @release $Release: 0.5.1 $ + */ +public interface Parser { + + public Object parse() throws SyntaxException; + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/PlainYamlParser.java b/javatoscachecker/kwalify/src/main/java/kwalify/PlainYamlParser.java new file mode 100644 index 0000000..6224044 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/PlainYamlParser.java @@ -0,0 +1,870 @@ +/* + * @(#)PlainYamlParser.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.regex.Matcher; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +/** + * plain yaml parser class which is a parent of YamlParser class. + * + * ex. + *
+ *  String str = kwalify.Util.readFile("document.yaml");
+ *  kwalify.Parser parser = new kwalify.PlainYamlParser(str);
+ *  Object doc = parser.parse();
+ * 
+ * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + */ +public class PlainYamlParser implements Parser { + + public static class Alias { + private String _label; + private int _linenum; + + public Alias(String label, int linenum) { + _label = label; + _linenum = linenum; + } + + public String getLabel() { return _label; } + public void setLabel(String label) { _label = label; } + + public int getLineNumber() { return _linenum; } + public void setLineNumber(int linenum) { _linenum = linenum; } + } + + + private String[] _lines; + private String _line = null; + private int _linenum = 0; + private Map _anchors = new HashMap(); + private Map _aliases = new HashMap(); // key: label, value: Integer + private Object _end_flag = null; + private String _sbuf = null; + private int _index = 0; + + public PlainYamlParser(String yaml_str) { + // split yaml_str into _lines + List list = Util.toListOfLines(yaml_str); + int len = list.size(); + _lines = new String[len + 1]; + for (int i = 0; i < len; i++) { + _lines[i + 1] = (String)list.get(i); + } + } + + public Object parse() throws SyntaxException { + Object data = parseChild(0); + if (data == null && _end_flag == ENDFLAG_DOC_BEGIN) { + data = parseChild(0); + } + if (_aliases.size() > 0) { + resolveAliases(data); + } + //System.err.println("*** debug: data = " + Util.inspect(data)); + //System.err.println("*** debug: data = " + data.toString()); + return data; + } + + public boolean hasNext() { + return _end_flag != ENDFLAG_EOF; + } + + public Object[] parseAll() throws SyntaxException { + List docs = new ArrayList(); + while (hasNext()) { + Object doc = parse(); + docs.add(doc); + } + return docs.toArray(); + } + + + protected List createSequence(int linenum) { + return new ArrayList(); + } + + //private List createSequence() { + // return createSequence(_linenum); + //} + + protected void addSequenceValue(List seq, Object value, int linenum) { + seq.add(value); + } + + protected void setSequenceValueAt(List seq, int index, Object value, int linenum) { + seq.set(index, value); + } + + protected Map createMapping(int linenum) { + return new DefaultableHashMap(); + } + + //private Map createMapping() { + // return createMapping(_linenum); + //} + + protected void setMappingValueWith(Map map, Object key, Object value, int linenum) { + map.put(key, value); + } + + protected void setMappingDefault(Map map, Object value, int linenum) { + if (map instanceof Defaultable) { + ((Defaultable)map).setDefault(value); + } + } + + protected void mergeMapping(Map map, Map map2, int linenum) { + for (Iterator it = map2.keySet().iterator(); it.hasNext(); ) { + Object key = it.next(); + if (! map.containsKey(key)) { + Object value = map2.get(key); + map.put(key, value); + } + } + } + + protected void mergeList(Map map, List maplist, int linenum) throws SyntaxException { + for (Iterator it = maplist.iterator(); it.hasNext(); ) { + Object elem = it.next(); + mergeCollection(map, elem, linenum); + } + } + + protected void mergeCollection(Map map, Object collection, int linenum) throws SyntaxException { + if (collection instanceof Map) { + mergeMapping(map, (Map)collection, linenum); + } else if (collection instanceof List) { + mergeList(map, (List)collection, linenum); + } else { + throw syntaxError("'<<' requires collection (mapping, or sequence of mapping)."); + } + } + + protected Object createScalar(Object value, int linenum) { + return value; + } + + private Object createScalar(Object value) { + return createScalar(value, _linenum); + } + + protected String currentLine() { + return _line; + } + + protected int currentLineNumber() { + return _linenum; + } + + protected String getLine() { + String line; + do { + line = _getLine_(); + } while (line != null && Util.matches(line, "^\\s*($|#)")); + return line; + } + + protected String _getLine_() { + if (++_linenum < _lines.length) { + _line = _lines[_linenum]; + if (Util.matches(_line, "^\\.\\.\\.$")) { + _line = null; + _end_flag = ENDFLAG_DOC_END; + } else if (Util.matches(_line, "^---( [!%].*)?$")) { + _line = null; + _end_flag = ENDFLAG_DOC_BEGIN; + } + } else { + _line = null; + _end_flag = ENDFLAG_EOF; + } + return _line; + } + + protected static final String ENDFLAG_EOF = ""; + protected static final String ENDFLAG_DOC_BEGIN = "---"; + protected static final String ENDFLAG_DOC_END = "..."; + + private void resetBuffer(String str) { + _sbuf = str.charAt(str.length() - 1) == '\n' ? str : str + "\n"; + _index = -1; + } + + + private int _getChar_() { + if (_index + 1 < _sbuf.length()) { + _index++; + } else { + String line = getLine(); + if (line == null) return -1; + resetBuffer(line); + _index++; + } + int ch = _sbuf.charAt(_index); + return ch; + } + + private int getChar() { + int ch; + do { + ch = _getChar_(); + } while (ch >= 0 && isWhite(ch)); + return ch; + } + + private int getCharOrNewline() { + int ch; + do { + ch = _getChar_(); + } while (ch >= 0 && isWhite(ch) && ch != '\n'); + return ch; + } + + private int currentChar() { + return _sbuf.charAt(_index); + } + + private SyntaxException syntaxError(String message, int linenum) { + return new YamlSyntaxException(message, linenum); + } + + private SyntaxException syntaxError(String message) { + return new SyntaxException(message, _linenum); + } + + private Object parseChild(int column) throws SyntaxException { + String line = getLine(); + if (line == null) { + return createScalar(null); + } + Matcher m = Util.matcher(line, "^( *)(.*)"); + if (! m.find()) { + assert false; + return null; + } + int indent = m.group(1).length(); + if (indent < column) { + return createScalar(null); + } + String value = m.group(2); + return parseValue(column, value, indent); + } + + private Object parseValue(int column, String value, int value_start_column) throws SyntaxException { + Object data; + if (Util.matches(value, "^-( |$)")) { + data = parseSequence(value_start_column, value); + } else if (Util.matches(value, "^((?::?[-.\\w]+|'.*?'|\".*?\"|=|<<) *):(( +)(.*))?$")) { + data = parseMapping(value_start_column, value); + } else if (Util.matches(value, "^[\\[\\{]")) { + data = parseFlowStyle(column, value); + } else if (Util.matches(value, "^\\&[-\\w]+( |$)")) { + data = parseAnchor(column, value); + } else if (Util.matches(value, "^\\*[-\\w]+( |$)")) { + data = parseAlias(column, value); + } else if (Util.matches(value, "^[|>]")) { + data = parseBlockText(column, value); + } else if (Util.matches(value, "^!")) { + data = parseTag(column, value); + } else if (Util.matches(value, "^\\#")) { + data = parseChild(column); + } else { + data = parseScalar(column, value); + } + return data; + } + + private static boolean isWhite(int ch) { + return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; + } + + + private Object parseFlowStyle(int column, String value) throws SyntaxException { + resetBuffer(value); + getChar(); + Object data = parseFlow(0); + int ch = currentChar(); + assert ch == ']' || ch == '}'; + ch = getCharOrNewline(); + if (ch != '\n' && ch != '#' && ch >= 0) { + throw syntaxError("flow style sequence is closed buf got '" + ((char)ch) + "'."); + } + if (ch >= 0) getLine(); + return data; + } + + private Object parseFlow(int depth) throws SyntaxException { + int ch = currentChar(); + //ch = getChar(); + if (ch < 0) { + throw syntaxError("found EOF when parsing flow style."); + } + Object data; + if (ch == '[') { + data = parseFlowSequence(depth); + } else if (ch == '{') { + data = parseFlowMapping(depth); + } else { + data = parseFlowScalar(depth); + } + return data; + } + + private List parseFlowSequence(int depth) throws SyntaxException { + assert currentChar() == '['; + List seq = createSequence(_linenum); + int ch = getChar(); + if (ch != '}') { + int linenum = currentLineNumber(); + //seq.add(parseFlowSequenceItem(depth+1); + addSequenceValue(seq, parseFlowSequenceItem(depth + 1), linenum); + while ((ch = currentChar()) == ',') { + ch = getChar(); + if (ch == '}') { + throw syntaxError("sequence item required (or last comma is extra)."); + } + //if (ch == '?') break; + linenum = currentLineNumber(); + //seq.add(parseFlowSequenceItem(depth+1); + addSequenceValue(seq, parseFlowSequenceItem(depth + 1), linenum); + } + } + if (currentChar() != ']') { + throw syntaxError("flow style sequence requires ']'."); + } + if (depth > 0) getChar(); + return seq; + } + + private Object parseFlowSequenceItem(int depth) throws SyntaxException { + return parseFlow(depth); + } + + private Map parseFlowMapping(int depth) throws SyntaxException { + assert currentChar() == '{'; + Map map = createMapping(_linenum); + int ch = getChar(); + if (ch != '}') { + int linenum = currentLineNumber(); + Object[] pair = parseFlowMappingItem(depth + 1); + Object key = pair[0]; + Object value = pair[1]; + //map[ke] = value + setMappingValueWith(map, key, value, linenum); + while ((ch = currentChar()) == ',') { + ch = getChar(); + if (ch == '}') { + throw syntaxError("mapping item required (or last comman is extra."); + } + //if (ch == '}') break; + linenum = currentLineNumber(); + pair = parseFlowMappingItem(depth + 1); + key = pair[0]; + value = pair[1]; + //map.put(key) = value; + setMappingValueWith(map, key, value, linenum); + } + } + if (currentChar() != '}') { + throw syntaxError("flow style mapping requires '}'."); + } + if (depth > 0) getChar(); + return map; + } + + private Object[] parseFlowMappingItem(int depth) throws SyntaxException { + Object key = parseFlow(depth); + int ch = currentChar(); + if (ch != ':') { + String s = ch >= 0 ? "'" + ((char)ch) + "'" : "EOF"; + throw syntaxError("':' expected but got " + s); + } + getChar(); + Object value = parseFlow(depth); + return new Object[] { key, value }; + } + + private Object parseFlowScalar(int depth) throws SyntaxException { + int ch = currentChar(); + Object scalar = null; + StringBuffer sb = new StringBuffer(); + if (ch == '"' || ch == '\'') { + int endch = ch; + while ((ch = _getChar_()) >= 0 && ch != endch) { + sb.append((char)ch); + } + getChar(); + scalar = sb.toString(); + } else { + sb.append((char)ch); + while ((ch = _getChar_()) >= 0 && ch != ':' && ch != ',' && ch != ']' && ch != '}') { + sb.append((char)ch); + } + scalar = toScalar(sb.toString().trim()); + } + return createScalar(scalar); + } + + private Object parseTag(int column, String value) throws SyntaxException { + assert Util.matches(value, "^!\\S+"); + Matcher m = Util.matcher(value, "^!(\\S+)((\\s+)(.*))?$"); + if (! m.find()) { + assert false; + return null; + } + String tag = m.group(1); + String space = m.group(3); + String value2 = m.group(4); + Object data; + if (value2 != null && value2.length() > 0) { + int value_start_column = column + 1 + tag.length() + space.length(); + data = parseValue(column, value2, value_start_column); + } else { + data = parseChild(column); + } + return data; + } + + private Object parseAnchor(int column, String value) throws SyntaxException { + assert Util.matches(value, "^\\&([-\\w]+)(( *)(.*))?$"); + Matcher m = Util.matcher(value, "^\\&([-\\w]+)(( *)(.*))?$"); + if (! m.find()) { + assert false; + return null; + } + String label = m.group(1); + String space = m.group(3); + String value2 = m.group(4); + Object data; + if (value2 != null && value2.length() > 0) { + int value_start_column = column + 1 + label.length() + space.length(); + data = parseValue(column, value2, value_start_column); + } else { + data = parseChild(column); + } + registerAnchor(label, data); + return data; + } + + private void registerAnchor(String label, Object data) throws SyntaxException { + if (_anchors.containsKey(label)) { + throw syntaxError("anchor '" + label + "' is already used."); + } + _anchors.put(label, data); + } + + private Object parseAlias(int column, String value) throws SyntaxException { + assert value.matches("^\\*([-\\w]+)(( *)(.*))?$"); + Matcher m = Util.matcher(value, "^\\*([-\\w]+)(( *)(.*))?$"); + if (! m.find()) { + assert false; + return null; + } + String label = m.group(1); + //String space = m.group(3); + String value2 = m.group(4); + if (value2 != null && value2.length() > 0 && value2.charAt(0) != '#') { + throw syntaxError("alias cannot take any data."); + } + Object data = _anchors.get(label); + if (data == null) { + //throw syntaxError("anchor '" + label "' not found (cannot refer to backward or child anchor)."); + data = registerAlias(label); + } + getLine(); + return data; + } + + private Alias registerAlias(String label) throws SyntaxException { + Integer count = (Integer)_aliases.get(label); + if (count == null) { + _aliases.put(label, new Integer(1)); + } else { + _aliases.put(label, new Integer(count.intValue() + 1)); + } + return new Alias(label, _linenum); + } + + + private void resolveAliases(Object data) throws SyntaxException { // List or Map + Map resolved = new IdentityHashMap(); + resolveAliases(data, resolved); + } + + + private void resolveAliases(Object data, Map resolved) throws SyntaxException { + if (resolved.containsKey(data)) { + return; + } + resolved.put(data, data); + if (data instanceof List) { + resolveAliases((List)data, resolved); + } else if (data instanceof Map) { + resolveAliases((Map)data, resolved); + } else { + assert !(data instanceof Alias); + } + if (data instanceof Defaultable) { + Object default_value = ((Defaultable)data).getDefault(); + if (default_value != null) { + resolveAliases(default_value, resolved); + } + } + } + + private void resolveAliases(List seq, Map resolved) throws SyntaxException { + int len = seq.size(); + for (int i = 0; i < len; i++) { // don't use itrator not to raise java.util.ConcurrentModificationException + Object val = seq.get(i); + if (val instanceof Alias) { + Alias alias = (Alias)val; + String label = alias.getLabel(); + if (_anchors.containsKey(label)) { + //seq.set(i, _anchors.get(label); + setSequenceValueAt(seq, i, _anchors.get(label), alias.getLineNumber()); + } else { + throw syntaxError("anchor '" + alias.getLabel() + "' not found."); + } + } else if (val instanceof List || val instanceof Map) { + resolveAliases(val, resolved); + } + } + } + + private void resolveAliases(Map map, Map resolved) throws SyntaxException { + for (Iterator it = map.keySet().iterator(); it.hasNext(); ) { + Object key = it.next(); + Object val = map.get(key); + if (val instanceof Alias) { + Alias alias = (Alias)val; + String label = alias.getLabel(); + if (_anchors.containsKey(label)) { + //map.put(key, _anchors.get(label)); + setMappingValueWith(map, key, _anchors.get(label), alias.getLineNumber()); + } else { + throw syntaxError("anchor '" + alias.getLabel() + "' not found.", alias.getLineNumber()); + } + } else if (val instanceof List || val instanceof Map) { + resolveAliases(val, resolved); + } + } + } + + + +/* + private Object parseBlockText(int column, String value) throws SyntaxException { + assert Util.matches(value, "^[>|\\|]"); + Matcher m = Util.matcher(value, "^([>|\\|])([-+]?)\\s*(.*)$"); + if (! m.find()) { + assert false; + return null; + } + String blockchar = m.group(1); + String indicator = m.group(2); + String sep = blockchar.equals("|") ? "\n" : " "; + //String text = m.group(3).length() > 0 ? "" : m.group(3) + sep; + String text = m.group(3); + StringBuffer sb = new StringBuffer(); + StringBuffer empty = new StringBuffer(); + int min_indent = -1; + String line; + Pattern pat2 = Pattern.compile("^( *)(.*)"); + while ((line = _getLine_()) != null) { + (m = pat2.matcher(line)).find(); + int indent = m.group(1).length(); + if (m.group(2).length() == 0) { + empty.append("\n"); + } else if (indent < column) { + break; + } else { + if (min_indent < 0 || min_indent > indent) { + min_indent = indent; + } + sb.append(empty.toString()); + sb.append(line); + empty.delete(0, empty.length()); + } + } + if (indicator.equals("+")) { + sb.append(empty); + } else if (indicator.equals("-")) { + sb.deleteCharAt(sb.length() - 1); + } + String s; + if (min_indent <= 0) { + s = sb.toString(); + } else { + StringBuffer regex = new StringBuffer("(?m)^"); + for (int i = 0; i < min_indent; i++) regex.append(" "); + s = sb.toString().replaceAll(regex.toString(), ""); + } + if (blockchar.equals(">")) { + StringBuffer sb2 = new StringBuffer(); + int len = s.length(); + int n = 0; + for (int i = 0; i < len; i++) { + char ch = s.charAt(i); + if (ch == '\n') { + n++; + } else { + if (n == 1) { + sb2.append(' '); n = 0; + } else if (n > 1) { + sb2.append('\n'); n = 0; + } + sb2.append(ch); + } + } + s = sb2.toString(); + } + if (currentLine() != null && Util.matches(currentLine(), "^\\s*#")) getLine(); + return createScalar(text + s); + } +*/ + + private Object parseBlockText(int column, String value) throws SyntaxException { + assert Util.matches(value, "^[>|]"); + Matcher m = Util.matcher(value, "^([>|])([-+]?)(\\d*)\\s*(.*)$"); + if (! m.find()) { + assert false; + return null; + } + char blockchar = m.group(1).length() > 0 ? m.group(1).charAt(0) : '\0'; + char indicator = m.group(2).length() > 0 ? m.group(2).charAt(0) : '\0'; + int indent = m.group(3).length() > 0 ? Integer.parseInt(m.group(3)) : -1; + String text = m.group(4); + char sep = blockchar == '|' ? '\n' : ' '; + String line; + StringBuffer sb = new StringBuffer(); + int n = 0; + while ((line = _getLine_()) != null) { + m = Util.matcher(line, "^( *)(.*)$"); + m.find(); + String space = m.group(1); + String str = m.group(2); + if (indent < 0) indent = space.length(); + if (str.length() == 0) { // empty line + n++; + } else { + int slen = space.length(); + if (slen < column) { + break; + } else if (slen < indent) { + throw syntaxError("invalid indent in block text."); + } else { + if (n > 0) { + if (blockchar == '>' && sb.length() > 0) { + sb.deleteCharAt(sb.length() - 1); + } + for (int i = 0; i < n; i++) { + sb.append('\n'); + } + n = 0; + } + str = line.substring(indent); + } + } + sb.append(str); + if (blockchar == '>') { + if (sb.charAt(sb.length() - 1) == '\n') { + sb.setCharAt(sb.length() - 1, ' '); + } + } + } + if (line != null && Util.matches(line, "^ *#")) { + getLine(); + } + switch (indicator) { + case '+': + if (n > 0) { + if (blockchar == '>') { + sb.setCharAt(sb.length() - 1, '\n'); + } + for (int i = 0; i < n; i++) { + sb.append('\n'); + } + } + break; + case '-': + if (sb.charAt(sb.length() - 1) == sep) { + sb.deleteCharAt(sb.length() - 1); + } + break; + default: + if (blockchar == '>') { + sb.setCharAt(sb.length() - 1, '\n'); + } + } + return createScalar(text + sb.toString()); + } + + + private List parseSequence(int column, String value) throws SyntaxException { + assert Util.matches(value, "^-(( +)(.*))?$"); + List seq = createSequence(_linenum); + while (true) { + Matcher m = Util.matcher(value, "^-(( +)(.*))?$"); + if (! m.find()) { + throw syntaxError("sequence item is expected."); + } + String space = m.group(2); + String value2 = m.group(3); + int column2 = column + 1; + int linenum = currentLineNumber(); + // + Object elem; + if (value2 == null || value2.length() == 0) { + elem = parseChild(column2); + } else { + int value_start_column = column2 + space.length(); + elem = parseValue(column2, value2, value_start_column); + } + addSequenceValue(seq, elem, linenum); + // + String line = currentLine(); + if (line == null) break; + Matcher m2 = Util.matcher(line, "^( *)(.*)"); + m2.find(); + int indent = m2.group(1).length(); + if (indent < column) { + break; + } else if (indent > column) { + throw syntaxError("invalid indent of sequence."); + } + value = m2.group(2); + } + return seq; + } + + + private Map parseMapping(int column, String value) throws SyntaxException { + assert Util.matches(value, "^((?::?[-.\\w]+|'.*?'|\".*?\"|=|<<) *):(( +)(.*))?$"); + Map map = createMapping(_linenum); + while (true) { + Matcher m = Util.matcher(value, "^((?::?[-.\\w]+|'.*?'|\".*?\"|=|<<) *):(( +)(.*))?$"); + if (! m.find()) { + throw syntaxError("mapping item is expected."); + } + String v = m.group(1).trim(); + Object key = toScalar(v); + String value2 = m.group(4); + int column2 = column + 1; + int linenum = currentLineNumber(); + // + Object elem; + if (value2 == null || value2.length() == 0) { + elem = parseChild(column2); + } else { + int value_start_column = column2 + m.group(1).length() + m.group(3).length(); + elem = parseValue(column2, value2, value_start_column); + } + if (v.equals("=")) { + setMappingDefault(map, elem, linenum); + } else if (v.equals("<<")) { + mergeCollection(map, elem, linenum); + } else { + setMappingValueWith(map, key, elem, linenum); + } + // + String line = currentLine(); + if (line == null) { + break; + } + Matcher m2 = Util.matcher(line, "^( *)(.*)"); + m2.find(); + int indent = m2.group(1).length(); + if (indent < column) { + break; + } else if (indent > column) { + throw syntaxError("invalid indent of mapping."); + } + value = m2.group(2); + } + return map; + } + + + private Object parseScalar(int indent, String value) throws SyntaxException { + Object data = createScalar(toScalar(value)); + getLine(); + return data; + } + + + private Object toScalar(String value) { + Matcher m; + if ((m = Util.matcher(value, "^\"(.*)\"([ \t]*#.*$)?")).find()) { + return m.group(1); + } else if ((m = Util.matcher(value, "^'(.*)'([ \t]*#.*$)?")).find()) { + return m.group(1); + } else if ((m = Util.matcher(value, "^(.*\\S)[ \t]*#")).find()) { + value = m.group(1); + } + // + if (Util.matches(value, "^-?0x\\d+$")) return new Integer(Integer.parseInt(value, 16)); + else if (Util.matches(value, "^-?0\\d+$")) return new Integer(Integer.parseInt(value, 8)); + else if (Util.matches(value, "^-?\\d+$")) return new Integer(Integer.parseInt(value, 10)); + else if (Util.matches(value, "^-?\\d+\\.\\d+$")) return new Double(Double.parseDouble(value)); + else if (Util.matches(value, "^(true|yes|on)$")) return Boolean.TRUE; + else if (Util.matches(value, "^(false|no|off)$")) return Boolean.FALSE; + else if (Util.matches(value, "^(null|~)$")) return null; + else if (Util.matches(value, "^:(\\w+)$")) return value; + else if ((m = Util.matcher(value, "^(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)$")).find()) { + int year = Integer.parseInt(m.group(1)); + int month = Integer.parseInt(m.group(2)); + int day = Integer.parseInt(m.group(3)); + Calendar cal = Calendar.getInstance(); + cal.set(year, month, day, 0, 0, 0); + Date date = cal.getTime(); + return date; + } else if ((m = Util.matcher(value, "^(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)(?:[Tt]|[ \t]+)(\\d\\d?):(\\d\\d):(\\d\\d)(\\.\\d*)?(?:Z|[ \t]*([-+]\\d\\d?)(?::(\\d\\d))?)?$")).find()) { + int year = Integer.parseInt(m.group(1)); + int month = Integer.parseInt(m.group(2)); + int day = Integer.parseInt(m.group(3)); + int hour = Integer.parseInt(m.group(4)); + int min = Integer.parseInt(m.group(5)); + int sec = Integer.parseInt(m.group(6)); + //int usec = Integer.parseInt(m.group(7)); + //int tzone_h = Integer.parseInt(m.group(8)); + //int tzone_m = Integer.parseInt(m.group(9)); + String timezone = "GMT" + m.group(8) + ":" + m.group(9); + Calendar cal = Calendar.getInstance(); + cal.set(year, month, day, hour, min, sec); + cal.setTimeZone(TimeZone.getTimeZone(timezone)); + Date date = cal.getTime(); + return date; + } else { + return value; + } + } + +/* + public static void main(String[] args) throws Exception { + String filename = args.length > 0 ? args[0] : "test.yaml"; + String s = Util.readFile(filename); + PlainYamlParser parser = new PlainYamlParser(s); + while (parser.hasNext()) { + Object doc = parser.parse(); + System.out.println(Util.inspect(doc)); + } + } +*/ + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/Rule.java b/javatoscachecker/kwalify/src/main/java/kwalify/Rule.java new file mode 100644 index 0000000..558525d --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/Rule.java @@ -0,0 +1,673 @@ +/* + * @(#)Rule.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.regex.PatternSyntaxException; + +/** + * rule for validation. + * Validator class generates rule instances. + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + */ +public class Rule { + + /* + * instance variables + */ + + private Rule _parent; + private String _name = null; + private String _desc = null; + private String _short = null; //added by jora: only used for map types + private String _ref = null; //added by jora: only used for ref types (references to other rules by name) + private boolean _required = false; + private String _type = null; + private Class _type_class = null; + private String _pattern = null; + private Pattern _pattern_regexp = null; + private List _enum = null; + private List _sequence = null; + private DefaultableHashMap _mapping = null; + private String _assert = null; + private Map _range = null; + private Map _length = null; + private boolean _ident = false; + private boolean _unique = false; + + + /* + * accessors + */ + + public String getName() { return _name; } + public void setName(String name) { _name = name; } + + public String getDesc() { return _desc; } + public void setDesc(String desc) { _desc = desc; } + + public String getShort() { return _short; } + public void setShort(String val) { _short = val; } + + public String getReference() { return _ref; } + public void setReference(String ref) { _ref = ref; } + + public boolean isRequired() { return _required; } + public void setRequired(boolean required) { _required = required; } + + public String getType() { return _type; } + public void setType(String type) { _type = type; } + + public Class getTypeClass() { return _type_class; } + public void setTypeClass(Class type_class) { _type_class = type_class; } + + public String getPattern() { return _pattern; } + public void setPattern(String pattern) { _pattern = pattern; } + + public Pattern getPatternRegexp() { return _pattern_regexp; } + public void setPatternRegexp(Pattern patternRegexp) { _pattern_regexp = patternRegexp; } + + public List getEnum() { return _enum; } + public void setEnum(List enumList) { _enum = enumList; } + + public List getSequence() { return _sequence; } + public void setSequence(List sequence) { _sequence = sequence; } + + public DefaultableHashMap getMapping() { return _mapping; } + public void setMapping(DefaultableHashMap mapping) { _mapping = mapping; } + + public String getAssert() { return _assert; } + public void setAssert(String assertString) { _assert = assertString; } + + public Map getRange() { return _range; } + public void setRange(Map range) { _range = range; } + + public Map getLength() { return _length; } + public void setLength(Map length) { _length = length; } + + public boolean isIdent() { return _ident; } + public void setIdent(boolean ident) { _ident = ident; } + + public boolean isUnique() { return _unique; } + public void setUnique(boolean unique) { _unique = unique; } + + + /* + * constructors + */ + + public Rule(Object schema, Rule parent) throws SchemaException { + if (schema != null) { + if (! (schema instanceof Map)) { + throw schemaError("schema.notmap", null, "/", null, null); + } + Map rule_table = new IdentityHashMap(); + init((Map)schema, "", rule_table); + } + _parent = parent; + } + + public Rule(Object schema) throws SchemaException { + this(schema, null); + } + + public Rule(Map schema, Rule parent) throws SchemaException { + if (schema != null) { + Map rule_table = new IdentityHashMap(); + init(schema, "", rule_table); + } + _parent = parent; + } + + public Rule(Map schema) throws SchemaException { + this(schema, null); + } + + public Rule() throws SchemaException { + this(null, null); + } + + /* + * constants + */ + + private static final int CODE_NAME = "name".hashCode(); + private static final int CODE_DESC = "desc".hashCode(); + private static final int CODE_SHORT = "short".hashCode(); //jora + private static final int CODE_RULE = "rule".hashCode(); //jora + private static final int CODE_REQUIRED = "required".hashCode(); + private static final int CODE_TYPE = "type".hashCode(); + private static final int CODE_PATTERN = "pattern".hashCode(); + private static final int CODE_ENUM = "enum".hashCode(); + private static final int CODE_SEQUENCE = "sequence".hashCode(); + private static final int CODE_MAPPING = "mapping".hashCode(); + private static final int CODE_ASSERT = "assert".hashCode(); + private static final int CODE_RANGE = "range".hashCode(); + private static final int CODE_LENGTH = "length".hashCode(); + private static final int CODE_IDENT = "ident".hashCode(); + private static final int CODE_UNIQUE = "unique".hashCode(); + + + + /* + * instance methods + */ + public Rule getParent() { // by jora + return this._parent; + } + + + private static SchemaException schemaError(String errorSymbol, Rule rule, String path, Object value, Object[] args) { + String msg = Messages.buildMessage(errorSymbol, value, args); + return new SchemaException(msg, path, value, rule, errorSymbol); + } + + + private void init(Object elem, String path, Map rule_table) throws SchemaException { + assert elem != null; + if (! (elem instanceof Map)) { + if (path == null || path.equals("")) { + path = "/"; + } + throw schemaError("schema.notmap", null, path, null, null); + } + init((Map)elem, path, rule_table); + + } + + + private void init(Map hash, String path, Map rule_table) throws SchemaException { + Rule rule = this; + rule_table.put(hash, rule); + + // 'type:' entry + Object type = hash.get("type"); + initTypeValue(type, rule, path); + + // other entries + for (Iterator it = hash.keySet().iterator(); it.hasNext(); ) { + Object key = it.next(); + Object value = hash.get(key); + int code = key.hashCode(); + + if (code == CODE_TYPE && key.equals("type")) { + // done + } else if (code == CODE_NAME && key.equals("name")) { + initNameValue(value, rule, path); + } else if (code == CODE_DESC && key.equals("desc")) { + initDescValue(value, rule, path); + } else if (code == CODE_SHORT && key.equals("short")) { + initShortValue(value, rule, path); + } else if (code == CODE_REQUIRED && key.equals("required")) { + initRequiredValue(value, rule, path); + } else if (code == CODE_PATTERN && key.equals("pattern")) { + initPatternValue(value, rule, path); + } else if (code == CODE_ENUM && key.equals("enum")) { + initEnumValue(value, rule, path); + } else if (code == CODE_ASSERT && key.equals("assert")) { + initAssertValue(value, rule, path); + } else if (code == CODE_RANGE && key.equals("range")) { + initRangeValue(value, rule, path); + } else if (code == CODE_LENGTH && key.equals("length")) { + initLengthValue(value, rule, path); + } else if (code == CODE_IDENT && key.equals("ident")) { + initIdentValue(value, rule, path); + } else if (code == CODE_UNIQUE && key.equals("unique")) { + initUniqueValue(value, rule, path); + } else if (code == CODE_SEQUENCE && key.equals("sequence")) { + rule = initSequenceValue(value, rule, path, rule_table); + } else if (code == CODE_MAPPING && key.equals("mapping")) { + rule = initMappingValue(value, rule, path, rule_table); + } else if (code == CODE_RULE && key.equals("rule")) { + rule = initReferenceValue(value, rule, path, rule_table); + } else { + // removed by 'jora': interferes with the usage of YAML anchor/alias in grammar files + // throw schemaError("key.unknown", rule, path + "/" + key, key.toString() + ":", null); + } + } + + // confliction check + checkConfliction(hash, rule, path); + } + + + private void initTypeValue(Object value, Rule rule, String path) throws SchemaException { + if (value == null) { + value = Types.getDefaultType(); + } + if (! (value instanceof String)) { + throw schemaError("type.notstr", rule, path + "/type", _type, null); + } + _type = (String)value; + _type_class = Types.typeClass(_type); + if (! Types.isBuiltinType(_type)) { + throw schemaError("type.unknown", rule, path + "/type", _type, null); + } + } + + + private void initNameValue(Object value, Rule rule, String path) throws SchemaException { + _name = value.toString(); + } + + + private void initDescValue(Object value, Rule rule, String path) throws SchemaException { + _desc = value.toString(); + } + + private void initShortValue(Object value, Rule rule, String path) throws SchemaException { + + //the short form specification is to be interpreted as key if the type is a map or as an + //index if the target is a sequence (as index 0 actually) + if (!Types.isCollectionType(_type)) { + throw schemaError("range.notcollection", rule, path + "/short", value, null); + } + //we should also verify that it points to a declared key of the mapping .. not really, as it would + //fail the overall grammar + _short = value.toString(); + } + + private void initRequiredValue(Object value, Rule rule, String path) throws SchemaException { + if (! (value instanceof Boolean)) { + throw schemaError("required.notbool", rule, path + "/required", value, null); + } + _required = ((Boolean)value).booleanValue(); + } + + + private void initPatternValue(Object value, Rule rule, String path) throws SchemaException { + if (! (value instanceof String)) { + throw schemaError("pattern.notstr", rule, path + "/pattern", value, null); + } + _pattern = (String)value; + Matcher m = Util.matcher(_pattern, "\\A/(.*)/([mi]?[mi]?)\\z"); + if (! m.find()) { + throw schemaError("pattern.notmatch", rule, path + "/pattern", value, null); + } + String pat = m.group(1); + String opt = m.group(2); + int flag = 0; + if (opt.indexOf('i') >= 0) { + flag += Pattern.CASE_INSENSITIVE; + } + if (opt.indexOf('m') >= 0) { + flag += Pattern.DOTALL; // not MULTILINE + } + try { + _pattern_regexp = Pattern.compile(pat, flag); + } catch (PatternSyntaxException ex) { + throw schemaError("pattern.syntaxerr", rule, path + "/pattern", value, null); + } + } + + + private void initEnumValue(Object value, Rule rule, String path) throws SchemaException { + if (! (value instanceof List)) { + throw schemaError("enum.notseq", rule, path + "/enum", value, null); + } + _enum = (List)value; + if (Types.isCollectionType(_type)) { + throw schemaError("enum.notscalar", rule, path, "enum:", null); + } + Map elem_table = new HashMap(); + for (Iterator it = _enum.iterator(); it.hasNext(); ) { + Object elem = it.next(); + if (! Util.isInstanceOf(elem, _type_class)) { + throw schemaError("enum.type.unmatch", rule, path + "/enum", elem, new Object[] { Types.typeName(_type) }); + } + if (elem_table.containsKey(elem)) { + throw schemaError("enum.duplicate", rule, path + "/enum", elem, null); + } + elem_table.put(elem, Boolean.TRUE); + } + } + + + private void initAssertValue(Object value, Rule rule, String path) throws SchemaException { + if (! (value instanceof String)) { + throw schemaError("assert.notstr", rule, path + "/assert", value, null); + } + _assert = (String)value; + if (! Util.matches(_assert, "\\bval\\b")) { + throw schemaError("assert.noval", rule, path + "/assert", value, null); + } + } + + + private void initRangeValue(Object value, Rule rule, String path) throws SchemaException { + if (! (value instanceof Map)) { + throw schemaError("range.notmap", rule, path + "/range", value, null); + } + if (Types.isCollectionType(_type) || _type.equals("bool")) { + throw schemaError("range.notscalar", rule, path, "range:", null); + } + _range = (Map)value; + for (Iterator it = _range.keySet().iterator(); it.hasNext(); ) { + Object rkey = it.next(); + Object rval = _range.get(rkey); + if (rkey.equals("max") || rkey.equals("min") || rkey.equals("max-ex") || rkey.equals("min-ex")) { + if (! Util.isInstanceOf(rval, _type_class)) { + String typename = Types.typeName(_type); + throw schemaError("range.type.unmatch", rule, path + "/range/" + rkey, rval, new Object[] { typename }); + } + } else { + throw schemaError("range.undefined", rule, path + "/range/" + rkey, rkey.toString() + ":", null); + } + } + if (_range.containsKey("max") && _range.containsKey("max-ex")) { + throw schemaError("range.twomax", rule, path + "/range", null, null); + } + if (_range.containsKey("min") && _range.containsKey("min-ex")) { + throw schemaError("range.twomin", rule, path + "/range", null, null); + } + // + Object max = _range.get("max"); + Object min = _range.get("min"); + Object max_ex = _range.get("max-ex"); + Object min_ex = _range.get("min-ex"); + Object[] args = null; + //String error_symbol = null; + if (max != null) { + if (min != null && Util.compareValues(max, min) < 0) { + args = new Object[] { max, min }; + throw schemaError("range.maxltmin", rule, path + "/range", null, args); + } else if (min_ex != null && Util.compareValues(max, min_ex) <= 0) { + args = new Object[] { max, min_ex }; + throw schemaError("range.maxleminex", rule, path + "/range", null, args); + } + } else if (max_ex != null) { + if (min != null && Util.compareValues(max_ex, min) <= 0) { + args = new Object[] { max_ex, min }; + throw schemaError("range.maxexlemin", rule, path + "/range", null, args); + } else if (min_ex != null && Util.compareValues(max_ex, min_ex) <= 0) { + args = new Object[] { max_ex, min_ex }; + throw schemaError("range.maxexleminex", rule, path + "/range", null, args); + } + } + } + + + private void initLengthValue(Object value, Rule rule, String path) throws SchemaException { + if (! (value instanceof Map)) { + throw schemaError("length.notmap", rule, path + "/length", value, null); + } + _length = (Map)value; + if (! (_type.equals("str") || _type.equals("text"))) { + throw schemaError("length.nottext", rule, path, "length:", null); + } + for (Iterator it = _length.keySet().iterator(); it.hasNext(); ) { + Object k = it.next(); + Object v = _length.get(k); + if (k.equals("max") || k.equals("min") || k.equals("max-ex") || k.equals("min-ex")) { + if (! (v instanceof Integer)) { + throw schemaError("length.notint", rule, path + "/length/" + k, v, null); + } + } else { + throw schemaError("length.undefined", rule, path + "/length/" + k, k + ":", null); + } + } + if (_length.containsKey("max") && _length.containsKey("max-ex")) { + throw schemaError("length.twomax", rule, path + "/length", null, null); + } + if (_length.containsKey("min") && _length.containsKey("min-ex")) { + throw schemaError("length.twomin", rule, path + "/length", null, null); + } + // + Integer max = (Integer)_length.get("max"); + Integer min = (Integer)_length.get("min"); + Integer max_ex = (Integer)_length.get("max-ex"); + Integer min_ex = (Integer)_length.get("min-ex"); + Object[] args = null; + //String error_symbol = null; + if (max != null) { + if (min != null && max.compareTo(min) < 0) { + args = new Object[] { max, min }; + throw schemaError("length.maxltmin", rule, path + "/length", null, args); + } else if (min_ex != null && max.compareTo(min_ex) <= 0) { + args = new Object[] { max, min_ex }; + throw schemaError("length.maxleminex", rule, path + "/length", null, args); + } + } else if (max_ex != null) { + if (min != null && max_ex.compareTo(min) <= 0) { + args = new Object[] { max_ex, min }; + throw schemaError("length.maxexlemin", rule, path + "/length", null, args); + } else if (min_ex != null && max_ex.compareTo(min_ex) <= 0) { + args = new Object[] { max_ex, min_ex }; + throw schemaError("length.maxexleminex", rule, path + "/length", null, args); + } + } + } + + + private void initIdentValue(Object value, Rule rule, String path) throws SchemaException { + if (value == null || ! (value instanceof Boolean)) { + throw schemaError("ident.notbool", rule, path + "/ident", value, null); + } + _ident = ((Boolean)value).booleanValue(); + _required = true; + if (Types.isCollectionType(_type)) { + throw schemaError("ident.notscalar", rule, path, "ident:", null); + } + if (path.equals("")) { + throw schemaError("ident.onroot", rule, "/", "ident:", null); + } + if (_parent == null || ! _parent.getType().equals("map")) { + throw schemaError("ident.notmap", rule, path, "ident:", null); + } + } + + + private void initUniqueValue(Object value, Rule rule, String path) throws SchemaException { + if (! (value instanceof Boolean)) { + throw schemaError("unique.notbool", rule, path + "/unique", value, null); + } + _unique = ((Boolean)value).booleanValue(); + if (Types.isCollectionType(_type)) { + throw schemaError("unique.notscalar", rule, path, "unique:", null); + } + if (path.equals("")) { + throw schemaError("unique.onroot", rule, "/", "unique:", null); + } + //if (_parent == null || _parent.getType() == "map") { + // throw schemaError("sequence.notseq", rule, path + "/unique", value); + //} + } + + + private Rule initSequenceValue(Object value, Rule rule, String path, Map rule_table) throws SchemaException { + if (value != null && ! (value instanceof List)) { + throw schemaError("sequence.notseq", rule, path + "/sequence", value.toString(), null); + } + _sequence = (List)value; + if (_sequence == null || _sequence.size() == 0) { + throw schemaError("sequence.noelem", rule, path + "/sequence", value, null); + } + if (_sequence.size() > 1) { + throw schemaError("sequence.toomany", rule, path + "/sequence", value, null); + } + Object elem = _sequence.get(0); + if (elem == null) { + elem = new HashMap(); + } + int i = 0; + // Rule rule; + rule = (Rule)rule_table.get(elem); + if (rule == null) { + rule = new Rule(null, this); + rule.init(elem, path + "/sequence/" + i, rule_table); + } + _sequence = new ArrayList(); + _sequence.add(rule); + return rule; + } + + + private Rule initMappingValue(Object value, Rule rule, String path, Map rule_table) throws SchemaException { + + // error check + if (value != null && !(value instanceof Map)) { + throw schemaError("mapping.notmap", rule, path + "/mapping", value.toString(), null); + } + Object default_value = null; + if (value instanceof Defaultable) { + default_value = ((Defaultable)value).getDefault(); + } + if (value == null || ((Map)value).size() == 0 && default_value == null) { + throw schemaError("mapping.noelem", rule, path + "/mapping", value, null); + } + // create hash of rule + _mapping = new DefaultableHashMap(); + if (default_value != null) { + rule = (Rule)rule_table.get(default_value); + if (rule == null) { + rule = new Rule(null, this); + rule.init(default_value, path + "/mapping/=", rule_table); + } + _mapping.setDefault(rule); + } + // put rules into _mapping + Map map = (Map)value; + for (Iterator it = map.keySet().iterator(); it.hasNext(); ) { + Object k = it.next(); + Object v = map.get(k); // DefaultableHashMap + if (v == null) { + v = new DefaultableHashMap(); + } + rule = (Rule)rule_table.get(v); + if (rule == null) { + rule = new Rule(null, this); + rule.init(v, path + "/mapping/" + k, rule_table); + } + if (k.equals("=")) { + _mapping.setDefault(rule); + } else { + _mapping.put(k, rule); + } + } + return rule; + } + + private Rule initReferenceValue(Object value, Rule rule, String path, Map rule_table) throws SchemaException { + + this._ref = (String)value; + if (this._ref == null) + throw schemaError("required.novalue", rule, path, "rule", null); + //make sure a rule with this name is in scope + Rule refed = (Rule) + rule_table.values().stream() + .filter(val -> ((Rule)val).getName() != null && ((Rule)val).getName().equals(this._ref)) + .findFirst() + .orElse(null); + if (null == refed) + throw schemaError("ref.nosuchrule", rule, path, "reference", new Object[] { value }); + + return rule; + } + + private void checkConfliction(Map hash, Rule rule, String path) { + if (_type.equals("seq")) { + if (! hash.containsKey("sequence")) throw schemaError("seq.nosequence", rule, path, null, null); + if (_enum != null) throw schemaError("seq.conflict", rule, path, "enum:", null); + if (_pattern != null) throw schemaError("seq.conflict", rule, path, "pattern:", null); + if (_mapping != null) throw schemaError("seq.conflict", rule, path, "mapping:", null); + if (_range != null) throw schemaError("seq.conflict", rule, path, "range:", null); + if (_length != null) throw schemaError("seq.conflict", rule, path, "length:", null); + } else if (_type.equals("map")) { + if (! hash.containsKey("mapping")) throw schemaError("map.nomapping", rule, path, null, null); + if (_enum != null) throw schemaError("map.conflict", rule, path, "enum:", null); + if (_pattern != null) throw schemaError("map.conflict", rule, path, "pattern:", null); + if (_sequence != null) throw schemaError("map.conflict", rule, path, "sequence:", null); + if (_range != null) throw schemaError("map.conflict", rule, path, "range:", null); + if (_length != null) throw schemaError("map.conflict", rule, path, "length:", null); + } else { + if (_sequence != null) throw schemaError("scalar.conflict", rule, path, "sequence:", null); + if (_mapping != null) throw schemaError("scalar.conflict", rule, path, "mapping:", null); + if (_enum != null) { + if (_range != null) throw schemaError("enum.conflict", rule, path, "range:", null); + if (_length != null) throw schemaError("enum.conflict", rule, path, "length:", null); + if (_pattern != null) throw schemaError("enum.conflict", rule, path, "pattern:", null); + } + } + } + + + public String inspect() { + StringBuffer sb = new StringBuffer(); + int level = 0; + Map done = new IdentityHashMap(); + inspect(sb, level, done); + return sb.toString(); + } + + private void inspect(StringBuffer sb, int level, Map done) { + done.put(this, Boolean.TRUE); + String indent = Util.repeatString(" ", level); + if (_name != null) { sb.append(indent).append("name: ").append(_name).append("\n"); } + if (_desc != null) { sb.append(indent).append("desc: ").append(_desc).append("\n"); } + if (_type != null) { sb.append(indent).append("type: ").append(_type).append("\n"); } + if (_required) { sb.append(indent).append("required: ").append(_required).append("\n"); } + if (_pattern != null) { sb.append(indent).append("pattern: ").append(_pattern).append("\n"); } + if (_pattern_regexp != null) { sb.append(indent).append("regexp: ").append(_pattern_regexp).append("\n"); } + if (_assert != null) { sb.append(indent).append("assert: ").append(_assert).append("\n"); } + if (_ident) { sb.append(indent).append("ident: ").append(_ident).append("\n"); } + if (_unique) { sb.append(indent).append("unique: ").append(_unique).append("\n"); } + if (_enum != null) { + sb.append(indent).append("enum:\n"); + for (Iterator it = _enum.iterator(); it.hasNext(); ) { + sb.append(indent).append(" - ").append(it.next().toString()).append("\n"); + } + } + if (_range != null) { + sb.append(indent).append("range: { "); + String[] keys = new String[] { "max", "max-ex", "min", "min-ex", }; + String colon = ""; + for (int i = 0; i < keys.length; i++) { + Object val = _range.get(keys[i]); + if (val != null) { + sb.append(colon).append(keys[i]).append(": ").append(val); + colon = ", "; + } + } + sb.append(" }\n"); + } + if (_sequence != null) { + for (Iterator it = _sequence.iterator(); it.hasNext(); ) { + Rule rule = (Rule)it.next(); + if (done.containsKey(rule)) { + sb.append(indent).append(" ").append("- ...\n"); + } else { + sb.append(indent).append(" ").append("- \n"); + rule.inspect(sb, level + 2, done); + } + } + } + if (_mapping != null) { + for (Iterator it = _mapping.entrySet().iterator(); it.hasNext(); ) { + Map.Entry entry = (Map.Entry)it.next(); + Object key = entry.getKey(); + Rule rule = (Rule)entry.getValue(); + sb.append(indent).append(" ").append(Util.inspect(key)); + if (done.containsKey(rule)) { + sb.append(": ...\n"); + } else { + sb.append(":\n"); + rule.inspect(sb, level + 2, done); + } + } + } + } + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/SchemaException.java b/javatoscachecker/kwalify/src/main/java/kwalify/SchemaException.java new file mode 100644 index 0000000..5d53bd1 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/SchemaException.java @@ -0,0 +1,22 @@ +/* + * @(#)SchemaException.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +/** + * exception class thrown by Rule constructor + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + */ +public class SchemaException extends BaseException { + private static final long serialVersionUID = 4750598728284538818L; + + public SchemaException(String message, String ypath, Object value, Rule rule, String errorSymbol) { + super(message, ypath, value, rule, errorSymbol); + } + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/SyntaxException.java b/javatoscachecker/kwalify/src/main/java/kwalify/SyntaxException.java new file mode 100644 index 0000000..8c36b66 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/SyntaxException.java @@ -0,0 +1,28 @@ +/* + * @(#)SyntaxException.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +/** + * exception class thrown by parser when syntax is wrong. + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + * @see Parser, YamlSyntaxException + */ +public class SyntaxException extends KwalifyException { + private static final long serialVersionUID = 2480059811372002740L; + + private int _linenum; + + public SyntaxException(String message, int linenum) { + super(message); + _linenum = linenum; + } + + public int getLineNumber() { return _linenum; } + public void setLineNumber(int linenum) { _linenum = linenum; } +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/Types.java b/javatoscachecker/kwalify/src/main/java/kwalify/Types.java new file mode 100644 index 0000000..fbe655c --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/Types.java @@ -0,0 +1,107 @@ +/* + * @(#)Types.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.Date; + +/** + * utility methods for type (str, int, ...). + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + */ +public class Types { + + public static Class typeClass(String type) { + return (Class)__type_classes.get(type); + } + + public static String typeName(String type) { + String name = (String)__type_names.get(type); + if (name == null) name = type; + return name; + } + + public static final String DEFAULT_TYPE = "str"; + + public static String getDefaultType() { return DEFAULT_TYPE; } + + private static Map __type_classes; + private static Map __type_names; + static { + // + __type_classes = new HashMap(); + __type_classes.put("str", String.class); + __type_classes.put("int", Integer.class); + __type_classes.put("float", Double.class); + __type_classes.put("number", Number.class); + __type_classes.put("text", null); + __type_classes.put("bool", Boolean.class); + __type_classes.put("map", Map.class); + __type_classes.put("seq", List.class); + __type_classes.put("timestamp", Date.class); + __type_classes.put("date", Date.class); + __type_classes.put("symbol", String.class); + __type_classes.put("scalar", null); + __type_classes.put("any", Object.class); + __type_classes.put("ref", Object.class); // by jora + //__type_classes.put("null", null); + + // + __type_names = new HashMap(); + __type_names.put("map", "mapping"); + __type_names.put("seq", "sequence"); + __type_names.put("str", "string"); + __type_names.put("int", "integer"); + __type_names.put("bool", "boolean"); + __type_names.put("ref", "reference"); //by jora + } + + + public static boolean isBuiltinType(String type) { + return __type_classes.containsKey(type); + } + + public static boolean isCollectionType(String type) { + return type.equals("map") || type.equals("seq"); + } + + public static boolean isMapType(String type) { + return type.equals("map"); + } + + public static boolean isScalarType(String type) { + return !isCollectionType(type); + } + + public static boolean isCollection(Object obj) { + return obj instanceof Map || obj instanceof List; + } + + public static boolean isScalar(Object obj) { + return !isCollection(obj); + } + + public static boolean isCorrectType(Object obj, String type) { + Class type_class = typeClass(type); + if (type_class != null) { + return type_class.isInstance(obj); + } + if (type.equals("null")) { + return obj == null; + } else if (type.equals("text")) { + return obj instanceof String || obj instanceof Number; + } else if (type.equals("scalar")) { + return obj instanceof Number || obj instanceof String || obj instanceof Boolean || obj instanceof Date; + } + return false; + } + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/Util.java b/javatoscachecker/kwalify/src/main/java/kwalify/Util.java new file mode 100644 index 0000000..c27c947 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/Util.java @@ -0,0 +1,646 @@ +/* + * @(#)Util.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +import java.util.Collections; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.Date; +import java.io.Reader; +import java.io.InputStreamReader; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.Writer; +import java.io.FileWriter; +import java.io.File; + + +/** + * set of utility methods + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + */ + +public class Util { + + /** + * inspect List or Map + */ + public static String inspect(Object obj) { + StringBuffer sb = new StringBuffer(); + inspect(obj, sb, null); + return sb.toString(); + } + + private static void inspect(Object obj, StringBuffer sb, Map done) { + if (obj == null) { + sb.append("nil"); // null? + } else if (obj instanceof String) { + inspect((String)obj, sb, done); + } else if (obj instanceof Map) { + if (done == null) { + done = new IdentityHashMap(); + } + if (done.containsKey(obj)) { + sb.append("{...}"); + } else { + done.put(obj, Boolean.TRUE); + inspect((Map)obj, sb, done); + } + } else if (obj instanceof List) { + if (done == null) { + done = new IdentityHashMap(); + } + if (done.containsKey(obj)) { + sb.append("[...]"); + } else { + done.put(obj, Boolean.TRUE); + inspect((List)obj, sb, done); + } + } else { + sb.append(obj.toString()); + } + } + + private static void inspect(Map map, StringBuffer sb, Map done) { + sb.append('{'); + List list = new ArrayList(map.keySet()); + Collections.sort(list); + int i = 0; + for (Iterator it = list.iterator(); it.hasNext(); i++) { + Object key = it.next(); + Object value = map.get(key); + if (i > 0) { + sb.append(", "); + } + inspect(key, sb, done); + sb.append("=>"); + inspect(value, sb, done); + } + sb.append('}'); + } + + private static void inspect(List list, StringBuffer sb, Map done) { + sb.append('['); + int i = 0; + for (Iterator it = list.iterator(); it.hasNext(); i++) { + if (i > 0) { + sb.append(", "); + } + Object item = it.next(); + inspect(item, sb, null); + } + sb.append(']'); + } + + private static void inspect(String str, StringBuffer sb, Map done) { + sb.append('"'); + for (int i = 0; i < str.length(); i++) { + char ch = str.charAt(i); + switch (ch) { + case '"': sb.append("\\\""); break; + case '\n': sb.append("\\n"); break; + case '\r': sb.append("\\r"); break; + case '\t': sb.append("\\t"); break; + default: sb.append(ch); break; + } + } + sb.append('"'); + } + + + + /** + * + */ + protected static HashMap __patterns = new HashMap(); + + /** + * match pattern and return Mather object. + * + * ex. + *
+     *   String target = " name = foo\n mail = foo@mail.com\m";
+     *   Matcher m = Util.matcher(target, "^\\s*(\\w+)\\s*=\\s*(.*)$");
+     *   while (m.find()) {
+     *     String key   = m.group(1);
+     *     String value = m.gropu(2);
+     *   }
+     *  
+ */ + public static Matcher matcher(String target, String regexp) { + Pattern pat = (Pattern)__patterns.get(regexp); + if (pat == null) { + pat = Pattern.compile(regexp); + __patterns.put(regexp, pat); + } + return pat.matcher(target); + } + + + public static Matcher matcher(String target, Pattern regexp) { + return regexp.matcher(target); + } + + + /** + * return if pattern matched or not. + * + * ex. + *
+     *   String target = " name = foo\n";
+     *   if (Util.matches(target, "^\\s*(\\w+)\\s*=\\s*(.*)$")) {
+     *     System.out.println("matched.");
+     *   }
+     *  
+ */ + public static boolean matches(String target, String regexp) { + Matcher m = matcher(target, regexp); + return m.find(); + } + + + public static boolean matches(String target, Pattern regexp) { + Matcher m = regexp.matcher(target); + return m.find(); + } + + + /** + * shift array and return new array shifted + */ + public static String[] arrayShift(String[] array) { + String[] new_array = new String[array.length - 1]; + for (int i = 0; i < new_array.length; i++) { + new_array[i] = array[i + 1]; + } + return new_array; + } + + + /** + * pop up array an dreturn new array popped + */ + public static String[] arrayPop(String[] array) { + String[] new_array = new String[array.length - 1]; + for (int i = 0; i < new_array.length; i++) { + new_array[i] = array[i]; + } + return new_array; + } + + + /** + * concatenate all elements of array with separator + */ + public static String join(Object[] array, String separator) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + sb.append(separator); + } + sb.append(array[i]); + } + return sb.toString(); + } + + + /** + * concatenate all elements of list with separator + */ + public static String join(List list, String separator) { + StringBuffer sb = new StringBuffer(); + int i = 0; + for (Iterator it = list.iterator(); it.hasNext(); i++) { + Object item = it.next(); + if (i > 0) { + sb.append(separator); + } + sb.append(item); + } + return sb.toString(); + } + + + /** + * split string into list of line + */ + public static List toListOfLines(String str) { + List list = new ArrayList(); + int len = str.length(); + int head = 0; + for (int i = 0; i < len; i++) { + char ch = str.charAt(i); + if (ch == '\n') { + int tail = i + 1; + String line = str.substring(head, tail); + list.add(line); + head = tail; + } + } + if (head != len) { + String line = str.substring(head, len); + list.add(line); + } + return list; + } + + + /** + * split string into array of line + */ + public static String[] toLines(String str) { + List list = toListOfLines(str); + String[] lines = new String[list.size()]; + list.toArray(lines); + return lines; + } + + + /** + * return object id + */ + public static Integer getId(Object obj) { + int id = System.identityHashCode(obj); + return new Integer(id); + } + + + /** + * return true if 'instance' is an instance of 'klass' + */ + public static boolean isInstanceOf(Object instance, Class klass) { + if (instance == null || klass == null) { + return false; + } + Class c = instance.getClass(); + if (klass.isInterface()) { + while (c != null) { + Class[] interfaces = c.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + if (interfaces[i] == klass) { + return true; + } + } + c = c.getSuperclass(); + } + } else { + while (c != null) { + if (c == klass) { + return true; + } + c = c.getSuperclass(); + } + } + return false; + } + + + /** + * read file content with default encoding of system + */ + public static String readFile(String filename) throws IOException { + String charset = System.getProperty("file.encoding"); + return readFile(filename, charset); + } + + + /** + * read file content with specified encoding + */ + public static String readFile(String filename, String encoding) throws IOException { + InputStream stream = null; + String content = null; + try { + stream = new FileInputStream(filename); + content = readInputStream(stream, encoding); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException ignore) {} + } + } + return content; + } + + /** + * + */ + public static String readInputStream(InputStream stream) throws IOException { + String encoding = System.getProperty("file.encoding"); + return readInputStream(stream, encoding); + } + + + /** + * + */ + public static String readInputStream(InputStream stream, String encoding) throws IOException { + Reader reader = null; + String content = null; + try { + reader = new InputStreamReader(stream, encoding); + StringBuffer sb = new StringBuffer(); + int ch; + while ((ch = reader.read()) >= 0) { + sb.append((char)ch); + } + content = sb.toString(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException ignore) {} + } + } + return content; + } + + + /** + * + */ + public static void writeFile(String filename, String content) throws IOException { + Writer writer = null; + try { + writer = new FileWriter(filename); + writer.write(content); + } finally { + if (writer != null) { + writer.close(); + } + } + } + + + public static void makeDir(String path) throws IOException { + File dir = new File(path); + dir.mkdir(); + } + + + public static void renameFile(String old_path, String new_path) throws IOException { + File old_file = new File(old_path); + File new_file = new File(new_path); + new_file.delete(); + old_file.renameTo(new_file); + } + + + public static void moveFile(String filepath, String dirpath) throws IOException { + File old_file = new File(filepath); + File new_file = new File(dirpath + "/" + old_file.getName()); + new_file.delete(); + old_file.renameTo(new_file); + } + + + public static String untabify(CharSequence str) { + return untabify(str, 8); + } + + + public static String untabify(CharSequence str, int tab_width) { + StringBuffer sb = new StringBuffer(); + int len = str.length(); + int col = -1; + for (int i = 0; i < len; i++) { + col = ++col % tab_width; + char ch = str.charAt(i); + //if (ch == '\t') { + // int n = tab_width - col; + // while (--n >= 0) + // sb.append(' '); + // col = -1; // reset col + //} else { + // sb.append(ch); + // if (ch == '\n') + // col = -1; // reset col + //} + switch (ch) { + case '\t': + int n = tab_width - col; + while (--n >= 0) { + sb.append(' '); + } + col = -1; // reset col + break; + case '\n': + sb.append(ch); + col = -1; // reset col; + break; + default: + sb.append(ch); + } + } + return sb.toString(); + } + + + private static final int VALUE_INTEGER = 1; + private static final int VALUE_DOUBLE = 2; + private static final int VALUE_STRING = 4; + private static final int VALUE_BOOLEAN = 8; + private static final int VALUE_DATE = 16; + private static final int VALUE_OBJECT = 32; + + public static int compare(Object value1, Object value2) throws InvalidTypeException { + if (! (value1 instanceof Comparable)) { + throw new InvalidTypeException(value1.toString() + "is not Comparable."); + } + if (! (value2 instanceof Comparable)) { + throw new InvalidTypeException(value2.toString() + "is not Comparable."); + } + return ((Comparable)value1).compareTo((Comparable)value2); + } + + public static int compareValues(Object value1, Object value2) throws InvalidTypeException { + int vtype = (valueType(value1) << 8) | valueType(value2); + switch (vtype) { + case (VALUE_INTEGER << 8) | VALUE_INTEGER : + return ((Integer)value1).compareTo((Integer)value2); + case (VALUE_DOUBLE << 8) | VALUE_DOUBLE : + return ((Double)value1).compareTo((Double)value2); + case (VALUE_STRING << 8) | VALUE_STRING : + return ((String)value1).compareTo((String)value2); + case (VALUE_BOOLEAN << 8) | VALUE_BOOLEAN : + //return ((Boolean)value1).compareTo((Boolean)value2); // J2SDK1.4 doesn't support Boolean#compareTo()! + boolean b1 = ((Boolean)value1).booleanValue(); + boolean b2 = ((Boolean)value2).booleanValue(); + return b1 == b2 ? 0 : (b1 ? 1 : -1); + //if (b1 == b2) return 0; + //if (b1 && !b2) return 1; + //if (!b1 && b2) return -1; + //assert false; + case (VALUE_DATE << 8) | VALUE_DATE : + return ((Date)value1).compareTo((Date)value2); + // + case (VALUE_DOUBLE << 8) | VALUE_INTEGER : + case (VALUE_INTEGER << 8) | VALUE_DOUBLE : + double d1 = ((Number)value1).doubleValue(); + double d2 = ((Number)value2).doubleValue(); + return d1 > d2 ? 1 : (d1 < d2 ? -1 : 0); + } + throw new InvalidTypeException("cannot compare '" + value1.getClass().getName() + "' with '" + value2.getClass().getName()); + } + + private static int valueType(Object value) { + if (value instanceof Integer) return VALUE_INTEGER; + if (value instanceof Double) return VALUE_DOUBLE; + if (value instanceof String) return VALUE_STRING; + if (value instanceof Boolean) return VALUE_BOOLEAN; + if (value instanceof Date) return VALUE_DATE; + return VALUE_OBJECT; + } + + public static String repeatString(String str, int times) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < times; i++) { + sb.append(str); + } + return sb.toString(); + } + + + public static String[] subarray(String[] array, int begin, int end) { + if (begin >= end) { + return null; + } + if (end > array.length) { + end = array.length; + } + int size = end - begin; + String[] array2 = new String[size]; + int i, j; + for (i = begin, j = 0; i < end; i++, j++) { + array2[j] = array[i]; + } + return array2; + } + + public static String[] subarray(String[] array, int begin) { + if (begin < 0) { + begin += array.length; + } + return subarray(array, begin, array.length); + } + + + /** + * parse command-line options. + * + * ex. + *
+     *   public static void main(String[] arg) {
+     *      String singles = "hv";    // options which takes no argument.
+     *      String requireds = "fI";  // options which requires an argument.
+     *      String optionals = "i";   // options which can take optional argument.
+     *      try {
+     *         Object[] ret = parseCommandOptions(args, singles, requireds, optionals);
+     *         Map options        = (Map)ret[0];
+     *         Map properties     = (Map)ret[1];
+     *         String[] filenames = (String[])ret[2];
+     *         //...
+     *      } catch (CommandOptionException ex) {
+     *         char option = ex.getOption();
+     *         String error_symbol = ex.getErrorSymbol();
+     *         Systen.err.println("*** error: " + ex.getMessage());
+     *      }
+     *   }
+     * 
+ * + * @param args command-line strings + * @param singles options which takes no argument + * @param requireds options which requires an argument. + * @param optionals otpions which can take optional argument. + * @return array of options(Map), properties(Map), and filenames(String[]) + */ + public static Object[] parseCommandOptions(String[] args, String singles, String requireds, String optionals) throws CommandOptionException { + Map options = new HashMap(); + Map properties = new HashMap(); + String[] filenames = null; + // + int i; + for (i = 0; i < args.length; i++) { + if (args[i].length() == 0 || args[i].charAt(0) != '-') { + break; + } + String opt = args[i]; + int len = opt.length(); + if (len == 1) { // option '-' means "don't parse arguments!" + i++; + break; + } + assert len > 1; + if (opt.charAt(1) == '-') { // properties (--pname=pvalue) + String pname; + Object pvalue; + int idx = opt.indexOf('='); + if (idx >= 0) { + pname = opt.substring(2, idx); + pvalue = idx + 1 < opt.length() ? opt.substring(idx + 1) : ""; + } else { + pname = opt.substring(2); + pvalue = Boolean.TRUE; + } + properties.put(pname, pvalue); + } else { // command-line options + for (int j = 1; j < len; j++) { + char ch = opt.charAt(j); + String chstr = Character.toString(ch); + if (singles != null && singles.indexOf(ch) >= 0) { + options.put(chstr, Boolean.TRUE); + } else if (requireds != null && requireds.indexOf(ch) >= 0) { + String arg = null; + if (++j < len) { + arg = opt.substring(j); + } else if (++i < args.length) { + arg = args[i]; + } else { + throw new CommandOptionException("-" + ch + ": filename required.", ch, "command.option.noarg"); + } + options.put(chstr, arg); + break; + } else if (optionals != null && optionals.indexOf(ch) >= 0) { + Object arg = null; + if (++j < len) { + arg = opt.substring(j); + } else { + arg = Boolean.TRUE; + } + options.put(chstr, arg); + break; + } else { + throw new CommandOptionException("-" + ch + "invalid option.", ch, "command.option.invalid"); + } + } + } + } + + // filenames + //String[] filenames = i == args.length ? new String[0] : Util.subarray(args, i); + assert i <= args.length; + int n = args.length - i; + filenames = new String[n]; + for (int j = 0; i < args.length; i++, j++) { + filenames[j] = args[i]; + } + + // + return new Object[] { options, properties, filenames }; + } + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/ValidationException.java b/javatoscachecker/kwalify/src/main/java/kwalify/ValidationException.java new file mode 100644 index 0000000..5723e01 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/ValidationException.java @@ -0,0 +1,26 @@ +/* + * @(#)ValidationException.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +/** + * exception class which represents validation error. + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + */ +public class ValidationException extends BaseException { + private static final long serialVersionUID = -2991121377463453973L; + + public ValidationException(String message, String path, Object value, Rule rule, String error_symbol) { + super(message, path, value, rule, error_symbol); + } + + public ValidationException(String message, String path) { + this(message, path, null, null, null); + } + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/Validator.java b/javatoscachecker/kwalify/src/main/java/kwalify/Validator.java new file mode 100644 index 0000000..1b3dd53 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/Validator.java @@ -0,0 +1,415 @@ +/* + * @(#)Validator.java $Rev: 3 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +import java.util.Map; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.LinkedList; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Collections; + +/** + * validation engine + * + * ex. + *
+ *
+ *    // load YAML document
+ *    String str = Util.readFile("document.yaml");
+ *    YamlParser parser = new YamlParser(str);
+ *    Object document = parser.parse();
+ *
+ *    // load schema
+ *    Object schema = YamlUtil.loadFile("schema.yaml");
+ *
+ *    // generate validator and validate document
+ *    Validator validator = new Validator(shema);
+ *    List errors = validator.validate(document);
+ *
+ *    // show errors
+ *    if (errors != null && errors.size() > 0) {
+ *        parser.setErrorsLineNumber(errors);
+ *        java.util.Collections.sort(errors);
+ *        for (Iterator it = errors.iterator(); it.hasNext(); ) {
+ *            ValidationError error = (ValidationError)it.next();
+ *            int linenum = error.getLineNumber();
+ *            String path = error.getPath();
+ *            String mesg = error.getMessage();
+ *            String s = "- (" + linenum + ") [" + path + "] " + mesg;
+ *            System.err.println(s);
+ *        }
+ *    }
+ *  
+ * + * @version $Rev: 3 $ + * @release $Release: 0.5.1 $ + */ +public class Validator { + private Rule _rule; + + public Validator(Map schema) throws SchemaException { + _rule = new Rule(schema); + } + + public Validator(Object schema) throws SchemaException { + _rule = new Rule(schema); + } + + public Rule getRule() { return _rule; } + //public void setRule(Rule rule) { _rule = rule; } + + public List validate(Object value) { + ValidationContext vctx = new ValidationContext(); + _validateRule(value, _rule, vctx); + return vctx.getErrors(); + } + + protected boolean preValidationHook(Object value, Rule rule, ValidationContext context) { + // nothing + return false; + } + + protected void postValidationHook(Object value, Rule rule, ValidationContext context) { + } + + private void _validateRule(Object value, Rule rule, ValidationContext context) { + //why is done necessary? why would one end up having to validate twice the same collection?? + if (Types.isCollection(value)) { + if (context.done(value)) + return; + } + if (rule.isRequired() && value == null) { + Object[] args = new Object[] { Types.typeName(rule.getType()) }; + context.addError("required.novalue", rule, value, args); + return; + } + + if (preValidationHook(value, rule, context)) { + /* a 'higher power says is ok */ + postValidationHook(value, rule, context); + return; + } + + //Class klass = rule.getTypeClass(); + //if (klass != null && value != null && !klass.isInstance(value)) { + + int n = context.errorCount(); + validateRule(value, rule, context); + if (context.errorCount() != n) { + return; + } + // + postValidationHook(value, rule, context); + } + + /* this is the default validation process */ + protected void validateRule(Object value, Rule rule, ValidationContext context) { + + if (value != null && ! Types.isCorrectType(value, rule.getType())) { + Object[] args = new Object[] { Types.typeName(rule.getType()) }; + context.addError("type.unmatch", rule, value, args); + return; + } + // + if (rule.getSequence() != null) { + assert value == null || value instanceof List; + validateSequence((List)value, rule, context); + } else if (rule.getMapping() != null) { + assert value == null || value instanceof Map; + validateMapping((Map)value, rule, context); + } else if (rule.getReference() != null) { + validateReference(value, rule, context); + } else { + validateScalar(value, rule, context); + } + } + + private void validateScalar(Object value, Rule rule, ValidationContext context) { + assert rule.getSequence() == null; + assert rule.getMapping() == null; + if (rule.getAssert() != null) { + //boolean result = evaluate(rule.getAssert()); + //if (! result) { + // errors.add("asset.failed", rule, path, value, new Object[] { rule.getAssert() }); + //} + } + if (rule.getEnum() != null) { + if (! rule.getEnum().contains(value)) { + //if (Util.matches(keyname, "\\A\\d+\\z") keyname = "enum"; + context.addError("enum.notexist", rule, value, new Object[] { context.getPathElement() }); + } + } + // + if (value == null) { + return; + } + // + if (rule.getPattern() != null) { + if (! Util.matches(value.toString(), rule.getPatternRegexp())) { + context.addError("pattern.unmatch", rule, value, new Object[] { rule.getPattern() }); + } + } + if (rule.getRange() != null) { + assert Types.isScalar(value); + Map range = rule.getRange(); + Object v; + if ((v = range.get("max")) != null && Util.compareValues(v, value) < 0) { + context.addError("range.toolarge", rule, value, new Object[] { v.toString() }); + } + if ((v = range.get("min")) != null && Util.compareValues(v, value) > 0) { + context.addError("range.toosmall", rule, value, new Object[] { v.toString() }); + } + if ((v = range.get("max-ex")) != null && Util.compareValues(v, value) <= 0) { + context.addError("range.toolargeex", rule, value, new Object[] { v.toString() }); + } + if ((v = range.get("min-ex")) != null && Util.compareValues(v, value) >= 0) { + context.addError("range.toosmallex", rule, value, new Object[] { v.toString() }); + } + } + if (rule.getLength() != null) { + assert value instanceof String; + Map length = rule.getLength(); + int len = value.toString().length(); + Integer v; + if ((v = (Integer)length.get("max")) != null && v.intValue() < len) { + context.addError("length.toolong", rule, value, new Object[] { new Integer(len), v }); + } + if ((v = (Integer)length.get("min")) != null && v.intValue() > len) { + context.addError("length.tooshort", rule, value, new Object[] { new Integer(len), v }); + } + if ((v = (Integer)length.get("max-ex")) != null && v.intValue() <= len) { + context.addError("length.toolongex", rule, value, new Object[] { new Integer(len), v }); + } + if ((v = (Integer)length.get("min-ex")) != null && v.intValue() >= len) { + context.addError("length.tooshortex", rule, value, new Object[] { new Integer(len), v }); + } + } + } + + + private void validateSequence(List sequence, Rule seq_rule, ValidationContext context) { + assert seq_rule.getSequence() instanceof List; + assert seq_rule.getSequence().size() == 1; + if (sequence == null) { + return; + } + Rule rule = (Rule)seq_rule.getSequence().get(0); + int i = 0; + for (Iterator it = sequence.iterator(); it.hasNext(); i++) { + Object val = it.next(); + context.addPathElement(String.valueOf(i)); + _validateRule(val, rule, context); // validate recursively + context.removePathElement(); + } + if (rule.getType().equals("map")) { + Map mapping = rule.getMapping(); + List unique_keys = new ArrayList(); + for (Iterator it = mapping.keySet().iterator(); it.hasNext(); ) { + Object key = it.next(); + Rule map_rule = (Rule)mapping.get(key); + if (map_rule.isUnique() || map_rule.isIdent()) { + unique_keys.add(key); + } + } + // + if (unique_keys.size() > 0) { + for (Iterator it = unique_keys.iterator(); it.hasNext(); ) { + Object key = it.next(); + Map table = new HashMap(); // val => index + int j = 0; + for (Iterator it2 = sequence.iterator(); it2.hasNext(); j++) { + Map map = (Map)it2.next(); + Object val = map.get(key); + if (val == null) { + continue; + } + if (table.containsKey(val)) { + String path = context.getPath(); + String prev_path = path + "/" + table.get(val) + "/" + key; + context.addPathElement(String.valueOf(j)) + .addPathElement(key.toString()); + context.addError("value.notunique", rule, val, new Object[] { prev_path }); + context.removePathElement() + .removePathElement(); + } else { + table.put(val, new Integer(j)); + } + } + } + } + } else if (rule.isUnique()) { + Map table = new HashMap(); // val => index + int j = 0; + for (Iterator it = sequence.iterator(); it.hasNext(); j++) { + Object val = it.next(); + if (val == null) { + continue; + } + if (table.containsKey(val)) { + String path = context.getPath(); + String prev_path = path + "/" + table.get(val); + context.addPathElement(String.valueOf(j)) + .addError("value.notunique", rule, val, new Object[] { prev_path }) + .removePathElement(); + } else { + table.put(val, new Integer(j)); + } + } + } + } + + + private void validateMapping(Map mapping, Rule map_rule, ValidationContext context) { + assert map_rule.getMapping() instanceof Map; + if (mapping == null) { + return; + } + Map m = map_rule.getMapping(); + for (Iterator it = m.keySet().iterator(); it.hasNext(); ) { + Object key = it.next(); + Rule rule = (Rule)m.get(key); + if (rule.isRequired() && !mapping.containsKey(key)) { + context.addError("required.nokey", rule, mapping, new Object[] { key }); + } + } + for (Iterator it = mapping.keySet().iterator(); it.hasNext(); ) { + Object key = it.next(); + Object val = mapping.get(key); + Rule rule = (Rule)m.get(key); + context.addPathElement(key.toString()); + if (rule == null) { + context.addError("key.undefined", rule, mapping, new Object[] { key.toString() + ":", map_rule.getName() + m.keySet().toString() }); + } else { + _validateRule(val, rule, context); // validate recursively + } + context.removePathElement(); + } + } + + private void validateReference(Object value, Rule ref_rule, ValidationContext context) { + //look only up the rule chain. This is a limitation + Rule refed = ref_rule; + while ((refed = refed.getParent()) != null) { + if (refed.getName() != null && refed.getName().equals(ref_rule.getReference())) { + validateRule(value, refed, context); + return; + } + } + context.addError("ref.nosuchrule", ref_rule, value, new Object[] { ref_rule.getReference() }); + } + + public class ValidationContext { + + private StringBuilder path = new StringBuilder(""); + private List errors = new LinkedList(); + private Map done = new IdentityHashMap(); //completion tracker + + private ValidationContext() { + } + + public String getPath() { + return this.path.toString(); + } + + public Validator getValidator() { + return Validator.this; + } + + public ValidationContext addPathElement(String theElement) { + this.path.append("/") + .append(theElement); + return this; + } + + public String getPathElement() { + int index = this.path.lastIndexOf("/"); + return index >= 0 ? this.path.substring(index + 1) : this.path.toString(); + } + + public ValidationContext removePathElement() { + int index = this.path.lastIndexOf("/"); + if (index >= 0) + this.path.delete(index, this.path.length()); + return this; + } + + protected ValidationContext addError(String error_symbol, Rule rule, Object value, Object[] args) { + addError( + new ValidationException( + Messages.buildMessage(error_symbol, value, args), getPath(), value, rule, error_symbol)); + return this; + } + + protected ValidationContext addError(String error_symbol, Rule rule, String relpath, Object value, Object[] args) { + addError( + new ValidationException( + Messages.buildMessage(error_symbol, value, args), getPath()+"/"+relpath, value, rule, error_symbol)); + return this; + } + + public ValidationContext addError(String message, Rule rule, Object value, Throwable cause) { + addError( + new ValidationException( + message + ((cause == null) ? "" : ", cause " + cause), getPath(), value, rule, "")); + return this; + } + + public ValidationContext addError(ValidationException theError) { + this.errors.add(theError); + return this; + } + + + public List getErrors() { + return Collections.unmodifiableList(this.errors); + } + + public boolean hasErrors() { + return this.errors.isEmpty(); + } + + public int errorCount() { + return this.errors.size(); + } + + private boolean done(Object theTarget) { + if (this.done.get(theTarget) != null) { + return true; + } + this.done.put(theTarget, Boolean.TRUE); + return false; + } + + private boolean isDone(Object theTarget) { + return this.done.get(theTarget) != null; + } + } + +/* + public static void main(String[] args) throws Exception { + Map schema = (Map)YamlUtil.loadFile("schema.yaml"); + Validator validator = new Validator(schema); + String filename = args.length > 0 ? args[0] : "document.yaml"; + Object document = YamlUtil.loadFile(filename); + List errors = validator.validate(document); + if (errors != null && errors.size() > 0) { + for (Iterator it = errors.iterator(); it.hasNext(); ) { + ValidationException error = (ValidationException)it.next(); + //String s = "- [" + error.getPath() + "] " + error.getMessage(); + String s = "- <" + error.getErrorSymbol() + ">[" + error.getPath() + "] " + error.getMessage(); + System.out.println(s); + } + } else { + System.out.println("validtion OK."); + } + } +*/ + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/YamlParser.java b/javatoscachecker/kwalify/src/main/java/kwalify/YamlParser.java new file mode 100644 index 0000000..fbe351c --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/YamlParser.java @@ -0,0 +1,156 @@ +/* + * @(#)YamlParser.java $Rev: 3 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ +package kwalify; + +import java.util.Map; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * yaml parser which can keep line number of path. + * + * ex. + *
+ *  String yaml_str = Util.readFile("document.yaml");
+ *  YamlParser parser = new YamlParser(yaml_str);
+ *  Object document = parser.parse();
+ *  
+ */ +public class YamlParser extends PlainYamlParser { + private Map _linenums_table = new IdentityHashMap(); // object => sequence or mapping + private int _first_linenum = -1; + private Object _document = null; + + public YamlParser(String yaml_str) { + super(yaml_str); + } + + public Object parse() throws SyntaxException { + _document = super.parse(); + return _document; + } + + protected String getLine() { + String line = super.getLine(); + if (_first_linenum < 0) { + _first_linenum = currentLineNumber(); + } + return line; + } + + + public int getPathLineNumber(String ypath) throws InvalidPathException { + if (_document == null) { + return -1; + } + if (ypath.length() == 0 || ypath.equals("/")) { + return 1; + } + String[] elems = ypath.split("/"); + String last_elem = elems.length > 0 ? elems[elems.length - 1] : null; + int i = ypath.charAt(0) == '/' ? 1 : 0; + int len = elems.length - 1; + Object c = _document; // collection + for ( /* nothing */ ; i < len; i++) { + if (c == null) { + throw new InvalidPathException(ypath); + } else if (c instanceof Map) { + c = ((Map)c).get(elems[i]); + } else if (c instanceof List) { + int index = Integer.parseInt(elems[i]); + if (index < 0 || ((List)c).size() < index) { + throw new InvalidPathException(ypath); + } + c = ((List)c).get(index); + } else { + throw new InvalidPathException(ypath); + } + } + + if (c == null) { + throw new InvalidPathException(ypath); + } + Object linenums = _linenums_table.get(c); // Map or List + int linenum = -1; + if (c instanceof Map) { + assert linenums instanceof Map; + Object d = ((Map)linenums).get(last_elem); + linenum = ((Integer)d).intValue(); + } else if (c instanceof List) { + assert linenums instanceof List; + int index = Integer.parseInt(last_elem); + if (index < 0 || ((List)linenums).size() <= index) { + throw new InvalidPathException(ypath); + } + Object d = ((List)linenums).get(index); + linenum = ((Integer)d).intValue(); + } else { + throw new InvalidPathException(ypath); + } + return linenum; + } + + public void setErrorsLineNumber(List errors) throws InvalidPathException { + for (Iterator it = errors.iterator(); it.hasNext(); ) { + ValidationException ex = (ValidationException)it.next(); + ex.setLineNumber(getPathLineNumber(ex.getPath())); + } + } + + protected List createSequence(int linenum) { + List seq = new ArrayList(); + _linenums_table.put(seq, new ArrayList()); + return seq; + } + + protected void addSequenceValue(List seq, Object value, int linenum) { + seq.add(value); + List linenums = (List)_linenums_table.get(seq); + linenums.add(new Integer(linenum)); + } + + protected void setSequenceValueAt(List seq, int index, Object value, int linenum) { + seq.set(index, value); + List linenums = (List)_linenums_table.get(seq); + linenums.set(index, new Integer(linenum)); + } + + protected Map createMapping(int linenum) { + Map map = super.createMapping(linenum); + _linenums_table.put(map, new HashMap()); + return map; + } + + protected void setMappingValueWith(Map map, Object key, Object value, int linenum) { + map.put(key, value); + Map linenums = (Map)_linenums_table.get(map); + assert linenums != null; + linenums.put(key, new Integer(linenum)); + } + + protected void setMappingDefault(Map map, Object value, int linenum) { + super.setMappingDefault(map, value, linenum); + Map linenums = (Map)_linenums_table.get(map); + linenums.put(new Character('='), new Integer(linenum)); + } + + protected void mergeMapping(Map map, Map map2, int linenum) { + Map linenums = (Map)_linenums_table.get(map); + Map linenums2 = (Map)_linenums_table.get(map2); + assert linenums2 != null; + for (Iterator it = map2.keySet().iterator(); it.hasNext(); ) { + Object key = it.next(); + if (! map.containsKey(key)) { + map.put(key, map2.get(key)); + linenums.put(key, linenums2.get(key)); + } + } + } + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/YamlSyntaxException.java b/javatoscachecker/kwalify/src/main/java/kwalify/YamlSyntaxException.java new file mode 100644 index 0000000..a8b1011 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/YamlSyntaxException.java @@ -0,0 +1,23 @@ +/* + * @(#)YamlSyntaxException.java $Rev: 4 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +/** + * exception class thrown by YamlParser when syntax of YAML document is wrong + * + * @revision $Rev: 4 $ + * @release $Release: 0.5.1 $ + * @see SyntaxException + */ +public class YamlSyntaxException extends SyntaxException { + private static final long serialVersionUID = 2951669148531823857L; + + public YamlSyntaxException(String message, int linenum) { + super(message, linenum); + } + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/YamlUtil.java b/javatoscachecker/kwalify/src/main/java/kwalify/YamlUtil.java new file mode 100644 index 0000000..90dc17c --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/YamlUtil.java @@ -0,0 +1,62 @@ +/* + * @(#)YamlUtil.java $Rev: 3 $ $Release: 0.5.1 $ + * + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.Reader; +import java.io.InputStreamReader; +import java.io.IOException; + +/** + * utilify class for yaml. + * + * @version $Rev: 3 $ + * @release $Release: 0.5.1 $ + */ +public class YamlUtil { + + public static Object load(String yaml_str) throws SyntaxException { + PlainYamlParser parser = new PlainYamlParser(yaml_str); + Object doc = parser.parse(); + return doc; + } + + public static Object loadFile(String filename, String charset) throws IOException, SyntaxException { + Object doc = null; + InputStream input = null; + Reader reader = null; + try { + input = new FileInputStream(filename); + reader = new InputStreamReader(input, charset); + StringBuffer sb = new StringBuffer(); + int ch; + while ((ch = reader.read()) >= 0) { + sb.append((char)ch); + } + doc = load(sb.toString()); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (Exception ignore) {} + } + if (input != null) { + try { + input.close(); + } catch (Exception ignore) {} + } + } + return doc; + } + + public static Object loadFile(String filename) throws IOException, SyntaxException { + String encoding = System.getProperty("file.encoding"); + return loadFile(filename, encoding); + } + +} diff --git a/javatoscachecker/kwalify/src/main/java/kwalify/messages.properties b/javatoscachecker/kwalify/src/main/java/kwalify/messages.properties new file mode 100644 index 0000000..c4b45c0 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/java/kwalify/messages.properties @@ -0,0 +1,110 @@ +command.help = \ + Usage1: %s [-hvstlE] -f schema.yaml doc.yaml [doc2.yaml ...]\n\ + Usage2: %s [-hvstlE] -m schema.yaml [schema2.yaml ...]\n\ + \ \ -h, --help : help\n\ + \ \ -v : version\n\ + \ \ -s : silent\n\ + \ \ -f schema.yaml : schema definition file\n\ + \ \ -m : meta-validation mode\n\ + \ \ -t : expand tab character automatically\n\ + \ \ -l : show linenumber when errored (experimental)\n\ + \ \ -E : show errors in emacs-style (implies '-l')\n +command.option.noaction = command-line option '-f' or '-m' required. +meta.empty = %s: empty. +meta.valid = %s: ok. +meta.invalid = %s: NG! +schema.empty = %s#%d: empty. +validation.empty = %s#%d: empty. +validation.valid = %s#%d: valid. +validation.invalid = %s#%d: INVALID +command.property.invalid = %s: invalid property. +command.option.noarg = -%s: argument required. +command.option.noschema = -%s: schema filename required. +command.option.invalid = -%s: invalid command option. +schema.notmap = schema definition is not a mapping. +key.unknown = unknown key. +type.notstr = not a string. +type.unknown = unknown type. +required.notbool = not a boolean. +pattern.notstr = not a string (or regexp) +pattern.notmatch = should be '/..../'. +pattern.syntaxerr = has regexp error. +enum.notseq = not a sequence. +enum.notscalar = not available with seq or map. +enum.type.unmatch = %s type expected. +enum.duplicate = duplicated enum value. +assert.notstr = not a string. +assert.noval = 'val' is not used. +assert.syntaxerr = expression syntax error. +range.notmap = not a mapping. +range.notscalar = is available only with scalar type. +range.notcollection = not a collection type. +range.type.unmatch = not a %s. +range.undefined = undefined key. +range.twomax = both 'max' and 'max-ex' are not available at once. +range.twomin = both 'min' and 'min-ex' are not available at once. +range.maxltmin = max '%s' is less than min '%s'. +range.maxleminex = max '%s' is less than or equal to min-ex '%s'. +range.maxexlemin = max-ex '%s' is less than or equal to min '%s'. +range.maxexleminex = max-ex '%s' is less than or equal to min-ex '%s'. +length.notmap = not a mapping. +length.nottext = is available only with string or text. +length.notint = not an integer. +length.undefined = undefined key. +length.twomax = both 'max' and 'max-ex' are not available at once. +length.twomin = both 'min' and 'min-ex' are not available at once. +length.maxltmin = max '%s' is less than min '%s'. +length.maxleminex = max '%s' is less than or equal to min-ex '%s'. +length.maxexlemin = max-ex '%s' is less than or equal to min '%s'. +length.maxexleminex = max-ex '%s' is less than or equal to min-ex '%s'. +ident.notbool = not a boolean. +ident.notscalar = is available only with a scalar type. +ident.onroot = is not available on root element. +ident.notmap = is available only with an element of mapping. +unique.notbool = not a boolean. +unique.notscalar = is available only with a scalar type. +unique.onroot = is not available on root element. +sequence.notseq = not a sequence. +sequence.noelem = required one element. +sequence.toomany = required just one element. +mapping.notmap = not a mapping. +mapping.noelem = required at least one element. +seq.nosequence = type 'seq' requires 'sequence:'. +seq.conflict = not available with sequence. +map.nomapping = type 'map' requires 'mapping:'. +map.conflict = not available with mapping. +scalar.conflict = not available with scalar type. +enum.conflict = not available with 'enum:'. +required.novalue = value required but none. +type.unmatch = not a %s. +assert.failed = assertion expression failed (%s). +enum.notexist = invalid %s value. +pattern.unmatch = not matched to pattern %s. +range.toolarge = too large (> max %s). +range.toosmall = too small (< min %s). +range.toolargeex = too large (>= max %s). +range.toosmallex = too small (<= min %s). +length.toolong = too long (length %d > max %d). +length.tooshort = too short (length %d < min %d). +length.toolongex = too long (length %d >= max %d). +length.tooshortex = too short (length %d <= min %d). +value.notunique = is already used at '%s'. +required.nokey = key '%s:' is required. +key.undefined = key '%s' is undefined. Expecting one of %s. +flow.hastail = flow style sequence is closed but got '%s'. +flow.eof = found EOF when parsing flow style. +flow.noseqitem = sequence item required (or last comma is extra). +flow.seqnotclosed = flow style sequence requires ']'. +flow.mapnoitem = mapping item required (or last comma is extra). +flow.mapnotclosed = flow style mapping requires '}'. +flow.nocolon = ':' expected but got '%s'. +anchor.duplicated = anchor '%s' is already used. +alias.extradata = alias cannot take any data. +anchor.notfound = anchor '%s' not found +sequence.noitem = sequence item is expected. +sequence.badindent = illegal indent of sequence. +mapping.noitem = mapping item is expected. +mapping.badindent = illegal indent of mapping. +collection.notcollection = not a collection +ref.nosuchrule = no rule '%s' in scope + diff --git a/javatoscachecker/kwalify/src/main/resources/kwalify/messages.properties b/javatoscachecker/kwalify/src/main/resources/kwalify/messages.properties new file mode 100644 index 0000000..c4b45c0 --- /dev/null +++ b/javatoscachecker/kwalify/src/main/resources/kwalify/messages.properties @@ -0,0 +1,110 @@ +command.help = \ + Usage1: %s [-hvstlE] -f schema.yaml doc.yaml [doc2.yaml ...]\n\ + Usage2: %s [-hvstlE] -m schema.yaml [schema2.yaml ...]\n\ + \ \ -h, --help : help\n\ + \ \ -v : version\n\ + \ \ -s : silent\n\ + \ \ -f schema.yaml : schema definition file\n\ + \ \ -m : meta-validation mode\n\ + \ \ -t : expand tab character automatically\n\ + \ \ -l : show linenumber when errored (experimental)\n\ + \ \ -E : show errors in emacs-style (implies '-l')\n +command.option.noaction = command-line option '-f' or '-m' required. +meta.empty = %s: empty. +meta.valid = %s: ok. +meta.invalid = %s: NG! +schema.empty = %s#%d: empty. +validation.empty = %s#%d: empty. +validation.valid = %s#%d: valid. +validation.invalid = %s#%d: INVALID +command.property.invalid = %s: invalid property. +command.option.noarg = -%s: argument required. +command.option.noschema = -%s: schema filename required. +command.option.invalid = -%s: invalid command option. +schema.notmap = schema definition is not a mapping. +key.unknown = unknown key. +type.notstr = not a string. +type.unknown = unknown type. +required.notbool = not a boolean. +pattern.notstr = not a string (or regexp) +pattern.notmatch = should be '/..../'. +pattern.syntaxerr = has regexp error. +enum.notseq = not a sequence. +enum.notscalar = not available with seq or map. +enum.type.unmatch = %s type expected. +enum.duplicate = duplicated enum value. +assert.notstr = not a string. +assert.noval = 'val' is not used. +assert.syntaxerr = expression syntax error. +range.notmap = not a mapping. +range.notscalar = is available only with scalar type. +range.notcollection = not a collection type. +range.type.unmatch = not a %s. +range.undefined = undefined key. +range.twomax = both 'max' and 'max-ex' are not available at once. +range.twomin = both 'min' and 'min-ex' are not available at once. +range.maxltmin = max '%s' is less than min '%s'. +range.maxleminex = max '%s' is less than or equal to min-ex '%s'. +range.maxexlemin = max-ex '%s' is less than or equal to min '%s'. +range.maxexleminex = max-ex '%s' is less than or equal to min-ex '%s'. +length.notmap = not a mapping. +length.nottext = is available only with string or text. +length.notint = not an integer. +length.undefined = undefined key. +length.twomax = both 'max' and 'max-ex' are not available at once. +length.twomin = both 'min' and 'min-ex' are not available at once. +length.maxltmin = max '%s' is less than min '%s'. +length.maxleminex = max '%s' is less than or equal to min-ex '%s'. +length.maxexlemin = max-ex '%s' is less than or equal to min '%s'. +length.maxexleminex = max-ex '%s' is less than or equal to min-ex '%s'. +ident.notbool = not a boolean. +ident.notscalar = is available only with a scalar type. +ident.onroot = is not available on root element. +ident.notmap = is available only with an element of mapping. +unique.notbool = not a boolean. +unique.notscalar = is available only with a scalar type. +unique.onroot = is not available on root element. +sequence.notseq = not a sequence. +sequence.noelem = required one element. +sequence.toomany = required just one element. +mapping.notmap = not a mapping. +mapping.noelem = required at least one element. +seq.nosequence = type 'seq' requires 'sequence:'. +seq.conflict = not available with sequence. +map.nomapping = type 'map' requires 'mapping:'. +map.conflict = not available with mapping. +scalar.conflict = not available with scalar type. +enum.conflict = not available with 'enum:'. +required.novalue = value required but none. +type.unmatch = not a %s. +assert.failed = assertion expression failed (%s). +enum.notexist = invalid %s value. +pattern.unmatch = not matched to pattern %s. +range.toolarge = too large (> max %s). +range.toosmall = too small (< min %s). +range.toolargeex = too large (>= max %s). +range.toosmallex = too small (<= min %s). +length.toolong = too long (length %d > max %d). +length.tooshort = too short (length %d < min %d). +length.toolongex = too long (length %d >= max %d). +length.tooshortex = too short (length %d <= min %d). +value.notunique = is already used at '%s'. +required.nokey = key '%s:' is required. +key.undefined = key '%s' is undefined. Expecting one of %s. +flow.hastail = flow style sequence is closed but got '%s'. +flow.eof = found EOF when parsing flow style. +flow.noseqitem = sequence item required (or last comma is extra). +flow.seqnotclosed = flow style sequence requires ']'. +flow.mapnoitem = mapping item required (or last comma is extra). +flow.mapnotclosed = flow style mapping requires '}'. +flow.nocolon = ':' expected but got '%s'. +anchor.duplicated = anchor '%s' is already used. +alias.extradata = alias cannot take any data. +anchor.notfound = anchor '%s' not found +sequence.noitem = sequence item is expected. +sequence.badindent = illegal indent of sequence. +mapping.noitem = mapping item is expected. +mapping.badindent = illegal indent of mapping. +collection.notcollection = not a collection +ref.nosuchrule = no rule '%s' in scope + -- cgit 1.2.3-korg