Created
August 24, 2016 08:26
-
-
Save kysnm/ba3cc5783a860bdc53ba20b0c2e63cc2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Only in ./json-path/src/main/java/com/jayway/jsonpath/internal/path/: ArrayIndexOperation.java | |
diff -u ./json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayPathToken.java ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/ArrayPathToken.java | |
--- ./json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayPathToken.java 2016-08-03 17:14:02.000000000 +0900 | |
+++ ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/ArrayPathToken.java 2016-08-24 14:33:21.000000000 +0900 | |
@@ -1,189 +1,24 @@ | |
-/* | |
- * Copyright 2011 the original author or authors. | |
- * Licensed under the Apache License, Version 2.0 (the "License"); | |
- * you may not use this file except in compliance with the License. | |
- * You may obtain a copy of the License at | |
- * | |
- * http://www.apache.org/licenses/LICENSE-2.0 | |
- * | |
- * Unless required by applicable law or agreed to in writing, software | |
- * distributed under the License is distributed on an "AS IS" BASIS, | |
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
- * See the License for the specific language governing permissions and | |
- * limitations under the License. | |
- */ | |
-package com.jayway.jsonpath.internal.path; | |
+package org.embulk.filter.column.path; | |
-import com.jayway.jsonpath.InvalidPathException; | |
-import com.jayway.jsonpath.PathNotFoundException; | |
-import com.jayway.jsonpath.internal.PathRef; | |
-import org.slf4j.Logger; | |
-import org.slf4j.LoggerFactory; | |
+public class ArrayPathToken extends PathToken | |
+{ | |
+ private final Integer index; | |
-import static java.lang.String.format; | |
- | |
-/** | |
- * | |
- */ | |
-public class ArrayPathToken extends PathToken { | |
- | |
- private static final Logger logger = LoggerFactory.getLogger(ArrayPathToken.class); | |
- | |
- private final ArraySliceOperation arraySliceOperation; | |
- private final ArrayIndexOperation arrayIndexOperation; | |
- | |
- ArrayPathToken(final ArraySliceOperation arraySliceOperation) { | |
- this.arraySliceOperation = arraySliceOperation; | |
- this.arrayIndexOperation = null; | |
+ public ArrayPathToken(Integer index) | |
+ { | |
+ this.index = index; | |
} | |
- ArrayPathToken(final ArrayIndexOperation arrayIndexOperation) { | |
- this.arrayIndexOperation = arrayIndexOperation; | |
- this.arraySliceOperation = null; | |
- } | |
+ public Integer index() { return index; } | |
@Override | |
- public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { | |
- if (! checkArrayModel(currentPath, model, ctx)) | |
- return; | |
- if(arraySliceOperation != null){ | |
- evaluateSliceOperation(currentPath, parent, model, ctx); | |
- } else { | |
- evaluateIndexOperation(currentPath, parent, model, ctx); | |
- } | |
- | |
- } | |
- | |
- public void evaluateIndexOperation(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { | |
- | |
- if (! checkArrayModel(currentPath, model, ctx)) | |
- return; | |
- | |
- if(arrayIndexOperation.isSingleIndexOperation()){ | |
- handleArrayIndex(arrayIndexOperation.indexes().get(0), currentPath, model, ctx); | |
- } else { | |
- for (Integer index : arrayIndexOperation.indexes()) { | |
- handleArrayIndex(index, currentPath, model, ctx); | |
- } | |
- } | |
- } | |
- | |
- public void evaluateSliceOperation(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { | |
- | |
- if (! checkArrayModel(currentPath, model, ctx)) | |
- return; | |
- | |
- switch (arraySliceOperation.operation()) { | |
- case SLICE_FROM: | |
- sliceFrom(arraySliceOperation, currentPath, parent, model, ctx); | |
- break; | |
- case SLICE_BETWEEN: | |
- sliceBetween(arraySliceOperation, currentPath, parent, model, ctx); | |
- break; | |
- case SLICE_TO: | |
- sliceTo(arraySliceOperation, currentPath, parent, model, ctx); | |
- break; | |
- } | |
- } | |
- | |
- public void sliceFrom(ArraySliceOperation operation, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { | |
- int length = ctx.jsonProvider().length(model); | |
- int from = operation.from(); | |
- if (from < 0) { | |
- //calculate slice start from array length | |
- from = length + from; | |
- } | |
- from = Math.max(0, from); | |
- | |
- logger.debug("Slice from index on array with length: {}. From index: {} to: {}. Input: {}", length, from, length - 1, toString()); | |
- | |
- if (length == 0 || from >= length) { | |
- return; | |
- } | |
- for (int i = from; i < length; i++) { | |
- handleArrayIndex(i, currentPath, model, ctx); | |
- } | |
- } | |
- | |
- public void sliceBetween(ArraySliceOperation operation, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { | |
- int length = ctx.jsonProvider().length(model); | |
- int from = operation.from(); | |
- int to = operation.to(); | |
- | |
- to = Math.min(length, to); | |
- | |
- if (from >= to || length == 0) { | |
- return; | |
- } | |
- | |
- logger.debug("Slice between indexes on array with length: {}. From index: {} to: {}. Input: {}", length, from, to, toString()); | |
- | |
- for (int i = from; i < to; i++) { | |
- handleArrayIndex(i, currentPath, model, ctx); | |
- } | |
- } | |
- | |
- public void sliceTo(ArraySliceOperation operation, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { | |
- int length = ctx.jsonProvider().length(model); | |
- if (length == 0) { | |
- return; | |
- } | |
- int to = operation.to(); | |
- if (to < 0) { | |
- //calculate slice end from array length | |
- to = length + to; | |
- } | |
- to = Math.min(length, to); | |
- | |
- logger.debug("Slice to index on array with length: {}. From index: 0 to: {}. Input: {}", length, to, toString()); | |
- | |
- for (int i = 0; i < to; i++) { | |
- handleArrayIndex(i, currentPath, model, ctx); | |
- } | |
- } | |
- | |
- @Override | |
- public String getPathFragment() { | |
- if(arrayIndexOperation != null){ | |
- return arrayIndexOperation.toString(); | |
- } else { | |
- return arraySliceOperation.toString(); | |
- } | |
- } | |
- | |
- @Override | |
- public boolean isTokenDefinite() { | |
- if(arrayIndexOperation != null){ | |
- return arrayIndexOperation.isSingleIndexOperation(); | |
- } else { | |
- return false; | |
- } | |
- } | |
+ public String getPathFragment() | |
+ { | |
+ StringBuilder sb = new StringBuilder(); | |
+ sb.append("["); | |
+ sb.append(index); | |
+ sb.append("]"); | |
- /** | |
- * Check if model is non-null and array. | |
- * @param currentPath | |
- * @param model | |
- * @param ctx | |
- * @return false if current evaluation call must be skipped, true otherwise | |
- * @throws PathNotFoundException if model is null and evaluation must be interrupted | |
- * @throws InvalidPathException if model is not an array and evaluation must be interrupted | |
- */ | |
- protected boolean checkArrayModel(String currentPath, Object model, EvaluationContextImpl ctx) { | |
- if (model == null){ | |
- if (! isUpstreamDefinite()) { | |
- return false; | |
- } else { | |
- throw new PathNotFoundException("The path " + currentPath + " is null"); | |
- } | |
- } | |
- if (!ctx.jsonProvider().isArray(model)) { | |
- if (! isUpstreamDefinite()) { | |
- return false; | |
- } else { | |
- throw new PathNotFoundException(format("Filter: %s can only be applied to arrays. Current context is: %s", toString(), model)); | |
- } | |
- } | |
- return true; | |
+ return sb.toString(); | |
} | |
} | |
Only in ./json-path/src/main/java/com/jayway/jsonpath/internal/path/: ArraySliceOperation.java | |
Only in ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/: CharacterIndex.java | |
diff -u ./json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/CompiledPath.java | |
--- ./json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java 2016-08-03 17:14:02.000000000 +0900 | |
+++ ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/CompiledPath.java 2016-08-24 14:33:21.000000000 +0900 | |
@@ -1,78 +1,68 @@ | |
-/* | |
- * Copyright 2011 the original author or authors. | |
- * Licensed under the Apache License, Version 2.0 (the "License"); | |
- * you may not use this file except in compliance with the License. | |
- * You may obtain a copy of the License at | |
- * | |
- * http://www.apache.org/licenses/LICENSE-2.0 | |
- * | |
- * Unless required by applicable law or agreed to in writing, software | |
- * distributed under the License is distributed on an "AS IS" BASIS, | |
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
- * See the License for the specific language governing permissions and | |
- * limitations under the License. | |
- */ | |
-package com.jayway.jsonpath.internal.path; | |
- | |
-import com.jayway.jsonpath.Configuration; | |
-import com.jayway.jsonpath.internal.EvaluationAbortException; | |
-import com.jayway.jsonpath.internal.EvaluationContext; | |
-import com.jayway.jsonpath.internal.Path; | |
-import com.jayway.jsonpath.internal.PathRef; | |
-import org.slf4j.Logger; | |
-import org.slf4j.LoggerFactory; | |
- | |
-public class CompiledPath implements Path { | |
- | |
- private static final Logger logger = LoggerFactory.getLogger(CompiledPath.class); | |
- | |
- private final RootPathToken root; | |
- | |
- private final boolean isRootPath; | |
- | |
- | |
- public CompiledPath(RootPathToken root, boolean isRootPath) { | |
- this.root = root; | |
- this.isRootPath = isRootPath; | |
- } | |
+package org.embulk.filter.column.path; | |
- @Override | |
- public boolean isRootPath() { | |
- return isRootPath; | |
- } | |
+import org.apache.commons.lang3.StringUtils; | |
- @Override | |
- public EvaluationContext evaluate(Object document, Object rootDocument, Configuration configuration, boolean forUpdate) { | |
- if (logger.isDebugEnabled()) { | |
- logger.debug("Evaluating path: {}", toString()); | |
- } | |
+// rename from RootPathToken | |
+public class CompiledPath extends PathToken | |
+{ | |
- EvaluationContextImpl ctx = new EvaluationContextImpl(this, rootDocument, configuration, forUpdate); | |
- try { | |
- PathRef op = ctx.forUpdate() ? PathRef.createRoot(rootDocument) : PathRef.NO_OP; | |
- root.evaluate("", op, document, ctx); | |
- } catch (EvaluationAbortException abort){}; | |
+ private PathToken tail; | |
+ private int tokenCount; | |
+ private final String rootToken; | |
- return ctx; | |
- } | |
- @Override | |
- public EvaluationContext evaluate(Object document, Object rootDocument, Configuration configuration){ | |
- return evaluate(document, rootDocument, configuration, false); | |
+ CompiledPath(char rootToken) | |
+ { | |
+ this.rootToken = Character.toString(rootToken); | |
+ ; | |
+ this.tail = this; | |
+ this.tokenCount = 1; | |
} | |
- @Override | |
- public boolean isDefinite() { | |
- return root.isPathDefinite(); | |
+ public PathToken getTail() { return tail; } | |
+ | |
+ public String getTailPath() { return tail.toString(); } | |
+ | |
+ public int getTokenCount() { return tokenCount; } | |
+ | |
+ public Long baseIndex() | |
+ { | |
+ if (tail instanceof ArrayPathToken) { | |
+ return ((ArrayPathToken) tail).index().longValue(); | |
+ } else { | |
+ return null; | |
+ } | |
} | |
- @Override | |
- public boolean isFunctionPath() { | |
- return root.isFunctionPath(); | |
+ public String getParentPath() | |
+ { | |
+ return StringUtils.removeEnd(this.toString(), tail.toString()); | |
+ } | |
+ | |
+ public CompiledPath append(PathToken next) | |
+ { | |
+ this.tail = tail.appendTailToken(next); | |
+ this.tokenCount++; | |
+ return this; | |
+ } | |
+ | |
+ public PathTokenAppender getPathTokenAppender() | |
+ { | |
+ return new PathTokenAppender() | |
+ { | |
+ @Override | |
+ public PathTokenAppender appendPathToken(PathToken next) | |
+ { | |
+ append(next); | |
+ return this; | |
+ } | |
+ }; | |
} | |
@Override | |
- public String toString() { | |
- return root.toString(); | |
+ public String getPathFragment() | |
+ { | |
+ return rootToken; | |
} | |
+ | |
} | |
Only in ./json-path/src/main/java/com/jayway/jsonpath/internal/path/: EvaluationContextImpl.java | |
Only in ./json-path/src/main/java/com/jayway/jsonpath/internal/path/: FunctionPathToken.java | |
Only in ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/: InvalidPathException.java | |
diff -u ./json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/PathCompiler.java | |
--- ./json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java 2016-08-12 17:11:54.000000000 +0900 | |
+++ ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/PathCompiler.java 2016-08-24 14:33:21.000000000 +0900 | |
@@ -1,33 +1,32 @@ | |
-package com.jayway.jsonpath.internal.path; | |
+package org.embulk.filter.column.path; | |
-import com.jayway.jsonpath.InvalidPathException; | |
-import com.jayway.jsonpath.Predicate; | |
-import com.jayway.jsonpath.internal.CharacterIndex; | |
-import com.jayway.jsonpath.internal.Path; | |
-import com.jayway.jsonpath.internal.Utils; | |
-import com.jayway.jsonpath.internal.filter.FilterCompiler; | |
-import com.jayway.jsonpath.internal.function.ParamType; | |
-import com.jayway.jsonpath.internal.function.Parameter; | |
- | |
-import java.util.ArrayList; | |
-import java.util.Collection; | |
-import java.util.LinkedList; | |
-import java.util.List; | |
+/* | |
+ Porting from https://github.com/jayway/JsonPath | |
+ equivalent version 2.2.0, latest commit is c2c1686 | |
+ | |
+ dropped features | |
+ - Filter | |
+ - Placeholder | |
+ - Function | |
+ - multi properties (ex. $.json1['a','b']) | |
+ - ArrayIndexOperation (ex. $.json1[0,1]) | |
+ - ArraySliceOperation (ex. $.json1[1:2] (start:end)) | |
+ */ | |
+ | |
+import org.embulk.filter.column.ColumnFilterPlugin.ColumnConfig; | |
+ | |
+import java.io.StringWriter; | |
import static java.lang.Character.isDigit; | |
-import static java.util.Arrays.asList; | |
-public class PathCompiler { | |
+public class PathCompiler | |
+{ | |
+ | |
private static final char DOC_CONTEXT = '$'; | |
- private static final char EVAL_CONTEXT = '@'; | |
private static final char OPEN_SQUARE_BRACKET = '['; | |
private static final char CLOSE_SQUARE_BRACKET = ']'; | |
- private static final char OPEN_PARENTHESIS = '('; | |
- private static final char CLOSE_PARENTHESIS = ')'; | |
- private static final char OPEN_BRACE = '{'; | |
- private static final char CLOSE_BRACE = '}'; | |
private static final char WILDCARD = '*'; | |
private static final char PERIOD = '.'; | |
@@ -35,51 +34,38 @@ | |
private static final char TAB = '\t'; | |
private static final char CR = '\r'; | |
private static final char LF = '\r'; | |
- private static final char BEGIN_FILTER = '?'; | |
- private static final char COMMA = ','; | |
- private static final char SPLIT = ':'; | |
private static final char MINUS = '-'; | |
private static final char SINGLE_QUOTE = '\''; | |
private static final char DOUBLE_QUOTE = '"'; | |
- private final LinkedList<Predicate> filterStack; | |
private final CharacterIndex path; | |
- private PathCompiler(String path, LinkedList<Predicate> filterStack) { | |
- this.filterStack = filterStack; | |
+ public static Boolean isJsonPathNotation(ColumnConfig config) | |
+ { | |
+ StringBuilder dotNotationRootPath = new StringBuilder(DOC_CONTEXT).append(PERIOD); | |
+ StringBuilder bracketNotationRootPath = new StringBuilder(DOC_CONTEXT).append(OPEN_SQUARE_BRACKET); | |
+ return config.getName().contains(dotNotationRootPath.toString()) || config.getName().contains(bracketNotationRootPath.toString()); | |
+ } | |
+ | |
+ private PathCompiler(String path) | |
+ { | |
this.path = new CharacterIndex(path); | |
} | |
- private Path compile() { | |
- RootPathToken root = readContextToken(); | |
- return new CompiledPath(root, root.getPathFragment().equals("$")); | |
+ private CompiledPath compile() | |
+ { | |
+ CompiledPath root = readContextToken(); | |
+ return root; | |
} | |
- public static Path compile(String path, final Predicate... filters) { | |
- try { | |
- path = path.trim(); | |
- if(!(path.charAt(0) == DOC_CONTEXT) && !(path.charAt(0) == EVAL_CONTEXT)){ | |
- path = "$." + path; | |
- } | |
- if(path.endsWith(".")){ | |
- fail("Path must not end with a '.' or '..'"); | |
- } | |
- LinkedList filterStack = new LinkedList<Predicate>(asList(filters)); | |
- Path p = new PathCompiler(path.trim(), filterStack).compile(); | |
- return p; | |
- } catch (Exception e) { | |
- InvalidPathException ipe; | |
- if (e instanceof InvalidPathException) { | |
- ipe = (InvalidPathException) e; | |
- } else { | |
- ipe = new InvalidPathException(e); | |
- } | |
- throw ipe; | |
- } | |
+ public static CompiledPath compile(String path) | |
+ { | |
+ return new PathCompiler(path).compile(); | |
} | |
- private void readWhitespace() { | |
+ private void readWhitespace() | |
+ { | |
while (path.inBounds()) { | |
char c = path.currentChar(); | |
if (!isWhitespace(c)) { | |
@@ -89,20 +75,27 @@ | |
} | |
} | |
- private Boolean isPathContext(char c) { | |
- return (c == DOC_CONTEXT || c == EVAL_CONTEXT); | |
+ private boolean isWhitespace(char c) | |
+ { | |
+ return (c == SPACE || c == TAB || c == LF || c == CR); | |
+ } | |
+ | |
+ private Boolean isDocContext(char c) | |
+ { | |
+ return c == DOC_CONTEXT; | |
} | |
- //[$ | @] | |
- private RootPathToken readContextToken() { | |
+ // $ | |
+ private CompiledPath readContextToken() | |
+ { | |
readWhitespace(); | |
- if (!isPathContext(path.currentChar())) { | |
- throw new InvalidPathException("Path must start with '$' or '@'"); | |
+ if (!isDocContext(path.currentChar())) { | |
+ fail("Path must start with '$'"); | |
} | |
- RootPathToken pathToken = PathTokenFactory.createRootPathToken(path.currentChar()); | |
+ CompiledPath pathToken = PathTokenFactory.createRootPathToken(path.currentChar()); | |
PathTokenAppender appender = pathToken.getPathTokenAppender(); | |
if (path.currentIsTail()) { | |
@@ -111,7 +104,7 @@ | |
path.incrementPosition(1); | |
- if(path.currentChar() != PERIOD && path.currentChar() != OPEN_SQUARE_BRACKET){ | |
+ if (path.currentChar() != PERIOD && path.currentChar() != OPEN_SQUARE_BRACKET) { | |
fail("Illegal character at position " + path.position() + " expected '.' or '["); | |
} | |
@@ -123,7 +116,8 @@ | |
// | |
// | |
// | |
- private boolean readNextToken(PathTokenAppender appender) { | |
+ private boolean readNextToken(PathTokenAppender appender) | |
+ { | |
char c = path.currentChar(); | |
@@ -132,8 +126,6 @@ | |
return readBracketPropertyToken(appender) || | |
readArrayToken(appender) || | |
readWildCardToken(appender) || | |
- readFilterToken(appender) || | |
- readPlaceholderToken(appender) || | |
fail("Could not parse token starting at position " + path.position() + ". Expected ?, ', 0-9, * "); | |
case PERIOD: | |
return readDotToken(appender) || | |
@@ -142,7 +134,7 @@ | |
return readWildCardToken(appender) || | |
fail("Could not parse token starting at position " + path.position()); | |
default: | |
- return readPropertyOrFunctionToken(appender) || | |
+ return readPropertyToken(appender) || | |
fail("Could not parse token starting at position " + path.position()); | |
} | |
} | |
@@ -150,16 +142,14 @@ | |
// | |
// . and .. | |
// | |
- private boolean readDotToken(PathTokenAppender appender) { | |
- if (path.currentCharIs(PERIOD) && path.nextCharIs(PERIOD)) { | |
- appender.appendPathToken(PathTokenFactory.crateScanToken()); | |
- path.incrementPosition(2); | |
- } else if (!path.hasMoreCharacters()) { | |
+ private boolean readDotToken(PathTokenAppender appender) | |
+ { | |
+ if (!path.hasMoreCharacters()) { | |
throw new InvalidPathException("Path must not end with a '."); | |
} else { | |
path.incrementPosition(1); | |
} | |
- if(path.currentCharIs(PERIOD)){ | |
+ if (path.currentCharIs(PERIOD)) { | |
throw new InvalidPathException("Character '.' on position " + path.position() + " is not valid."); | |
} | |
return readNextToken(appender); | |
@@ -168,7 +158,8 @@ | |
// | |
// fooBar or fooBar() | |
// | |
- private boolean readPropertyOrFunctionToken(PathTokenAppender appender) { | |
+ private boolean readPropertyToken(PathTokenAppender appender) | |
+ { | |
if (path.currentCharIs(OPEN_SQUARE_BRACKET) || path.currentCharIs(WILDCARD) || path.currentCharIs(PERIOD) || path.currentCharIs(SPACE)) { | |
return false; | |
} | |
@@ -176,297 +167,35 @@ | |
int readPosition = startPosition; | |
int endPosition = 0; | |
- boolean isFunction = false; | |
- | |
while (path.inBounds(readPosition)) { | |
char c = path.charAt(readPosition); | |
if (c == SPACE) { | |
throw new InvalidPathException("Use bracket notion ['my prop'] if your property contains blank characters. position: " + path.position()); | |
- } | |
- else if (c == PERIOD || c == OPEN_SQUARE_BRACKET) { | |
+ } else if (c == PERIOD || c == OPEN_SQUARE_BRACKET) { | |
endPosition = readPosition; | |
break; | |
} | |
- else if (c == OPEN_PARENTHESIS) { | |
- isFunction = true; | |
- endPosition = readPosition++; | |
- break; | |
- } | |
readPosition++; | |
} | |
if (endPosition == 0) { | |
endPosition = path.length(); | |
} | |
- | |
- List<Parameter> functionParameters = null; | |
- if (isFunction) { | |
- if (path.inBounds(readPosition+1)) { | |
- // read the next token to determine if we have a simple no-args function call | |
- char c = path.charAt(readPosition + 1); | |
- if (c != CLOSE_PARENTHESIS) { | |
- path.setPosition(endPosition+1); | |
- // parse the arguments of the function - arguments that are inner queries or JSON document(s) | |
- String functionName = path.subSequence(startPosition, endPosition).toString(); | |
- functionParameters = parseFunctionParameters(functionName); | |
- } else { | |
- path.setPosition(readPosition + 1); | |
- } | |
- } | |
- else { | |
- path.setPosition(readPosition); | |
- } | |
- } | |
- else { | |
- path.setPosition(endPosition); | |
- } | |
+ path.setPosition(endPosition); | |
String property = path.subSequence(startPosition, endPosition).toString(); | |
- if(isFunction){ | |
- appender.appendPathToken(PathTokenFactory.createFunctionPathToken(property, functionParameters)); | |
- } else { | |
- appender.appendPathToken(PathTokenFactory.createSinglePropertyPathToken(property, SINGLE_QUOTE)); | |
- } | |
- | |
- return path.currentIsTail() || readNextToken(appender); | |
- } | |
- /** | |
- * Parse the parameters of a function call, either the caller has supplied JSON data, or the caller has supplied | |
- * another path expression which must be evaluated and in turn invoked against the root document. In this tokenizer | |
- * we're only concerned with parsing the path thus the output of this function is a list of parameters with the Path | |
- * set if the parameter is an expression. If the parameter is a JSON document then the value of the cachedValue is | |
- * set on the object. | |
- * | |
- * Sequence for parsing out the parameters: | |
- * | |
- * This code has its own tokenizer - it does some rudimentary level of lexing in that it can distinguish between JSON block parameters | |
- * and sub-JSON blocks - it effectively regex's out the parameters into string blocks that can then be passed along to the appropriate parser. | |
- * Since sub-jsonpath expressions can themselves contain other function calls this routine needs to be sensitive to token counting to | |
- * determine the boundaries. Since the Path parser isn't aware of JSON processing this uber routine is needed. | |
- * | |
- * Parameters are separated by COMMAs ',' | |
- * | |
- * <pre> | |
- * doc = {"numbers": [1,2,3,4,5,6,7,8,9,10]} | |
- * | |
- * $.sum({10}, $.numbers.avg()) | |
- * </pre> | |
- * | |
- * The above is a valid function call, we're first summing 10 + avg of 1...10 (5.5) so the total should be 15.5 | |
- * | |
- * @return | |
- * An ordered list of parameters that are to processed via the function. Typically functions either process | |
- * an array of values and/or can consume parameters in addition to the values provided from the consumption of | |
- * an array. | |
- */ | |
- private List<Parameter> parseFunctionParameters(String funcName) { | |
- PathToken currentToken; | |
- ParamType type = null; | |
- | |
- // Parenthesis starts at 1 since we're marking the start of a function call, the close paren will denote the | |
- // last parameter boundary | |
- Integer groupParen = 1, groupBracket = 0, groupBrace = 0, groupQuote = 0; | |
- Boolean endOfStream = false; | |
- char priorChar = 0; | |
- List<Parameter> parameters = new ArrayList<Parameter>(); | |
- StringBuffer parameter = new StringBuffer(); | |
- while (path.inBounds() && !endOfStream) { | |
- char c = path.currentChar(); | |
- path.incrementPosition(1); | |
- | |
- // we're at the start of the stream, and don't know what type of parameter we have | |
- if (type == null) { | |
- if (isWhitespace(c)) { | |
- continue; | |
- } | |
- | |
- if (c == OPEN_BRACE || isDigit(c) || DOUBLE_QUOTE == c) { | |
- type = ParamType.JSON; | |
- } | |
- else if (isPathContext(c)) { | |
- type = ParamType.PATH; // read until we reach a terminating comma and we've reset grouping to zero | |
- } | |
- } | |
- | |
- switch (c) { | |
- case DOUBLE_QUOTE: | |
- if (priorChar != '\\' && groupQuote > 0) { | |
- if (groupQuote == 0) { | |
- throw new InvalidPathException("Unexpected quote '\"' at character position: " + path.position()); | |
- } | |
- groupQuote--; | |
- } | |
- else { | |
- groupQuote++; | |
- } | |
- break; | |
- case OPEN_PARENTHESIS: | |
- groupParen++; | |
- break; | |
- case OPEN_BRACE: | |
- groupBrace++; | |
- break; | |
- case OPEN_SQUARE_BRACKET: | |
- groupBracket++; | |
- break; | |
- | |
- case CLOSE_BRACE: | |
- if (0 == groupBrace) { | |
- throw new InvalidPathException("Unexpected close brace '}' at character position: " + path.position()); | |
- } | |
- groupBrace--; | |
- break; | |
- case CLOSE_SQUARE_BRACKET: | |
- if (0 == groupBracket) { | |
- throw new InvalidPathException("Unexpected close bracket ']' at character position: " + path.position()); | |
- } | |
- groupBracket--; | |
- break; | |
- | |
- // In either the close paren case where we have zero paren groups left, capture the parameter, or where | |
- // we've encountered a COMMA do the same | |
- case CLOSE_PARENTHESIS: | |
- groupParen--; | |
- if (0 != groupParen) { | |
- parameter.append(c); | |
- } | |
- case COMMA: | |
- // In this state we've reach the end of a function parameter and we can pass along the parameter string | |
- // to the parser | |
- if ((0 == groupQuote && 0 == groupBrace && 0 == groupBracket | |
- && ((0 == groupParen && CLOSE_PARENTHESIS == c) || 1 == groupParen))) { | |
- endOfStream = (0 == groupParen); | |
- | |
- if (null != type) { | |
- Parameter param = null; | |
- switch (type) { | |
- case JSON: | |
- // parse the json and set the value | |
- param = new Parameter(parameter.toString()); | |
- break; | |
- case PATH: | |
- LinkedList<Predicate> predicates = new LinkedList<Predicate>(); | |
- PathCompiler compiler = new PathCompiler(parameter.toString(), predicates); | |
- param = new Parameter(compiler.compile()); | |
- break; | |
- } | |
- if (null != param) { | |
- parameters.add(param); | |
- } | |
- parameter.delete(0, parameter.length()); | |
- type = null; | |
- } | |
- } | |
- break; | |
- } | |
- | |
- if (type != null && !(c == COMMA && 0 == groupBrace && 0 == groupBracket && 1 == groupParen)) { | |
- parameter.append(c); | |
- } | |
- priorChar = c; | |
- } | |
- if (0 != groupBrace || 0 != groupParen || 0 != groupBracket) { | |
- throw new InvalidPathException("Arguments to function: '" + funcName + "' are not closed properly."); | |
- } | |
- return parameters; | |
- } | |
- | |
- private boolean isWhitespace(char c) { | |
- return (c == SPACE || c == TAB || c == LF || c == CR); | |
- } | |
- | |
- // | |
- // [?], [?,?, ..] | |
- // | |
- private boolean readPlaceholderToken(PathTokenAppender appender) { | |
- | |
- if (!path.currentCharIs(OPEN_SQUARE_BRACKET)) { | |
- return false; | |
- } | |
- int questionmarkIndex = path.indexOfNextSignificantChar(BEGIN_FILTER); | |
- if (questionmarkIndex == -1) { | |
- return false; | |
- } | |
- char nextSignificantChar = path.nextSignificantChar(questionmarkIndex); | |
- if (nextSignificantChar != CLOSE_SQUARE_BRACKET && nextSignificantChar != COMMA) { | |
- return false; | |
- } | |
- | |
- int expressionBeginIndex = path.position() + 1; | |
- int expressionEndIndex = path.nextIndexOf(expressionBeginIndex, CLOSE_SQUARE_BRACKET); | |
- | |
- if (expressionEndIndex == -1) { | |
- return false; | |
- } | |
- | |
- String expression = path.subSequence(expressionBeginIndex, expressionEndIndex).toString(); | |
- | |
- String[] tokens = expression.split(","); | |
- | |
- if (filterStack.size() < tokens.length) { | |
- throw new InvalidPathException("Not enough predicates supplied for filter [" + expression + "] at position " + path.position()); | |
- } | |
- | |
- Collection<Predicate> predicates = new ArrayList<Predicate>(); | |
- for (String token : tokens) { | |
- token = token != null ? token.trim() : token; | |
- if (!"?".equals(token == null ? "" : token)) { | |
- throw new InvalidPathException("Expected '?' but found " + token); | |
- } | |
- predicates.add(filterStack.pop()); | |
- } | |
- | |
- appender.appendPathToken(PathTokenFactory.createPredicatePathToken(predicates)); | |
- | |
- path.setPosition(expressionEndIndex + 1); | |
- | |
- return path.currentIsTail() || readNextToken(appender); | |
- } | |
- | |
- // | |
- // [?(...)] | |
- // | |
- private boolean readFilterToken(PathTokenAppender appender) { | |
- if (!path.currentCharIs(OPEN_SQUARE_BRACKET) && !path.nextSignificantCharIs(BEGIN_FILTER)) { | |
- return false; | |
- } | |
- | |
- int openStatementBracketIndex = path.position(); | |
- int questionMarkIndex = path.indexOfNextSignificantChar(BEGIN_FILTER); | |
- if (questionMarkIndex == -1) { | |
- return false; | |
- } | |
- int openBracketIndex = path.indexOfNextSignificantChar(questionMarkIndex, OPEN_PARENTHESIS); | |
- if (openBracketIndex == -1) { | |
- return false; | |
- } | |
- int closeBracketIndex = path.indexOfClosingBracket(openBracketIndex, true, true); | |
- if (closeBracketIndex == -1) { | |
- return false; | |
- } | |
- if (!path.nextSignificantCharIs(closeBracketIndex, CLOSE_SQUARE_BRACKET)) { | |
- return false; | |
- } | |
- int closeStatementBracketIndex = path.indexOfNextSignificantChar(closeBracketIndex, CLOSE_SQUARE_BRACKET); | |
- | |
- String criteria = path.subSequence(openStatementBracketIndex, closeStatementBracketIndex + 1).toString(); | |
- | |
- | |
- Predicate predicate = FilterCompiler.compile(criteria); | |
- appender.appendPathToken(PathTokenFactory.createPredicatePathToken(predicate)); | |
- | |
- path.setPosition(closeStatementBracketIndex + 1); | |
+ appender.appendPathToken(PathTokenFactory.createPropertyPathToken(property, SINGLE_QUOTE)); | |
return path.currentIsTail() || readNextToken(appender); | |
- | |
} | |
// | |
// [*] | |
// * | |
// | |
- private boolean readWildCardToken(PathTokenAppender appender) { | |
+ private boolean readWildCardToken(PathTokenAppender appender) | |
+ { | |
boolean inBracket = path.currentCharIs(OPEN_SQUARE_BRACKET); | |
@@ -493,15 +222,16 @@ | |
} | |
// | |
- // [1], [1,2, n], [1:], [1:2], [:2] | |
+ // [1] | |
// | |
- private boolean readArrayToken(PathTokenAppender appender) { | |
+ private boolean readArrayToken(PathTokenAppender appender) | |
+ { | |
if (!path.currentCharIs(OPEN_SQUARE_BRACKET)) { | |
return false; | |
} | |
char nextSignificantChar = path.nextSignificantChar(); | |
- if (!isDigit(nextSignificantChar) && nextSignificantChar != MINUS && nextSignificantChar != SPLIT) { | |
+ if (!isDigit(nextSignificantChar) && nextSignificantChar != MINUS) { | |
return false; | |
} | |
@@ -521,7 +251,7 @@ | |
//check valid chars | |
for (int i = 0; i < expression.length(); i++) { | |
char c = expression.charAt(i); | |
- if (!isDigit(c) && c != COMMA && c != MINUS && c != SPLIT && c != SPACE) { | |
+ if (!isDigit(c) && c != MINUS && c != SPACE) { | |
return false; | |
} | |
} | |
@@ -529,11 +259,9 @@ | |
boolean isSliceOperation = expression.contains(":"); | |
if (isSliceOperation) { | |
- ArraySliceOperation arraySliceOperation = ArraySliceOperation.parse(expression); | |
- appender.appendPathToken(PathTokenFactory.createSliceArrayPathToken(arraySliceOperation)); | |
+ fail("slice is not supported"); | |
} else { | |
- ArrayIndexOperation arrayIndexOperation = ArrayIndexOperation.parse(expression); | |
- appender.appendPathToken(PathTokenFactory.createIndexArrayPathToken(arrayIndexOperation)); | |
+ appender.appendPathToken(PathTokenFactory.createIndexArrayPathToken(parseInteger(expression))); | |
} | |
path.setPosition(expressionEndIndex + 1); | |
@@ -541,19 +269,29 @@ | |
return path.currentIsTail() || readNextToken(appender); | |
} | |
+ private static Integer parseInteger(String token) | |
+ { | |
+ try { | |
+ return Integer.parseInt(token); | |
+ } catch (Exception e) { | |
+ throw new InvalidPathException("Failed to parse token in ArrayIndexOperation: " + token, e); | |
+ } | |
+ } | |
+ | |
// | |
// ['foo'] | |
// | |
- private boolean readBracketPropertyToken(PathTokenAppender appender) { | |
+ private boolean readBracketPropertyToken(PathTokenAppender appender) | |
+ { | |
if (!path.currentCharIs(OPEN_SQUARE_BRACKET)) { | |
return false; | |
} | |
char potentialStringDelimiter = path.nextSignificantChar(); | |
if (potentialStringDelimiter != SINGLE_QUOTE && potentialStringDelimiter != DOUBLE_QUOTE) { | |
- return false; | |
+ return false; | |
} | |
- List<String> properties = new ArrayList<String>(); | |
+ String property = ""; | |
int startPosition = path.position() + 1; | |
int readPosition = startPosition; | |
@@ -565,45 +303,121 @@ | |
while (path.inBounds(readPosition)) { | |
char c = path.charAt(readPosition); | |
- if(inEscape){ | |
+ if (inEscape) { | |
inEscape = false; | |
- } else if('\\' == c){ | |
+ } else if ('\\' == c) { | |
inEscape = true; | |
} else if (c == CLOSE_SQUARE_BRACKET && !inProperty) { | |
- if (lastSignificantWasComma){ | |
- fail("Found empty property at index "+readPosition); | |
+ if (lastSignificantWasComma) { | |
+ fail("Found empty property at index " + readPosition); | |
} | |
break; | |
} else if (c == potentialStringDelimiter) { | |
if (inProperty && !inEscape) { | |
endPosition = readPosition; | |
String prop = path.subSequence(startPosition, endPosition).toString(); | |
- properties.add(Utils.unescape(prop)); | |
+ property = unescape(prop); | |
inProperty = false; | |
} else { | |
startPosition = readPosition + 1; | |
inProperty = true; | |
lastSignificantWasComma = false; | |
} | |
- } else if (c == COMMA){ | |
- if (lastSignificantWasComma){ | |
- fail("Found empty property at index "+readPosition); | |
- } | |
- lastSignificantWasComma = true; | |
+ | |
} | |
readPosition++; | |
} | |
int endBracketIndex = path.indexOfNextSignificantChar(endPosition, CLOSE_SQUARE_BRACKET) + 1; | |
+ if (endBracketIndex < endPosition) { | |
+ fail("endBracketIndex must be greater than endPosition " + path); | |
+ } | |
+ | |
path.setPosition(endBracketIndex); | |
- appender.appendPathToken(PathTokenFactory.createPropertyPathToken(properties, potentialStringDelimiter)); | |
+ appender.appendPathToken(PathTokenFactory.createPropertyPathToken(property, SINGLE_QUOTE)); | |
return path.currentIsTail() || readNextToken(appender); | |
} | |
- public static boolean fail(String message) { | |
+ public static String unescape(String str) | |
+ { | |
+ if (str == null) { | |
+ return null; | |
+ } | |
+ int len = str.length(); | |
+ StringWriter writer = new StringWriter(len); | |
+ StringBuffer unicode = new StringBuffer(4); | |
+ boolean hadSlash = false; | |
+ boolean inUnicode = false; | |
+ for (int i = 0; i < len; i++) { | |
+ char ch = str.charAt(i); | |
+ if (inUnicode) { | |
+ unicode.append(ch); | |
+ if (unicode.length() == 4) { | |
+ try { | |
+ int value = Integer.parseInt(unicode.toString(), 16); | |
+ writer.write((char) value); | |
+ unicode.setLength(0); | |
+ inUnicode = false; | |
+ hadSlash = false; | |
+ } catch (NumberFormatException nfe) { | |
+ throw new InvalidPathException("Unable to parse unicode value: " + unicode, nfe); | |
+ } | |
+ } | |
+ continue; | |
+ } | |
+ if (hadSlash) { | |
+ hadSlash = false; | |
+ switch (ch) { | |
+ case '\\': | |
+ writer.write('\\'); | |
+ break; | |
+ case '\'': | |
+ writer.write('\''); | |
+ break; | |
+ case '\"': | |
+ writer.write('"'); | |
+ break; | |
+ case 'r': | |
+ writer.write('\r'); | |
+ break; | |
+ case 'f': | |
+ writer.write('\f'); | |
+ break; | |
+ case 't': | |
+ writer.write('\t'); | |
+ break; | |
+ case 'n': | |
+ writer.write('\n'); | |
+ break; | |
+ case 'b': | |
+ writer.write('\b'); | |
+ break; | |
+ case 'u': { | |
+ inUnicode = true; | |
+ break; | |
+ } | |
+ default: | |
+ writer.write(ch); | |
+ break; | |
+ } | |
+ continue; | |
+ } else if (ch == '\\') { | |
+ hadSlash = true; | |
+ continue; | |
+ } | |
+ writer.write(ch); | |
+ } | |
+ if (hadSlash) { | |
+ writer.write('\\'); | |
+ } | |
+ return writer.toString(); | |
+ } | |
+ | |
+ public static boolean fail(String message) | |
+ { | |
throw new InvalidPathException(message); | |
} | |
} | |
diff -u ./json-path/src/main/java/com/jayway/jsonpath/internal/path/PathToken.java ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/PathToken.java | |
--- ./json-path/src/main/java/com/jayway/jsonpath/internal/path/PathToken.java 2016-08-03 17:14:02.000000000 +0900 | |
+++ ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/PathToken.java 2016-08-24 14:38:07.000000000 +0900 | |
@@ -1,217 +1,49 @@ | |
-/* | |
- * Copyright 2011 the original author or authors. | |
- * Licensed under the Apache License, Version 2.0 (the "License"); | |
- * you may not use this file except in compliance with the License. | |
- * You may obtain a copy of the License at | |
- * | |
- * http://www.apache.org/licenses/LICENSE-2.0 | |
- * | |
- * Unless required by applicable law or agreed to in writing, software | |
- * distributed under the License is distributed on an "AS IS" BASIS, | |
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
- * See the License for the specific language governing permissions and | |
- * limitations under the License. | |
- */ | |
-package com.jayway.jsonpath.internal.path; | |
- | |
-import com.jayway.jsonpath.Option; | |
-import com.jayway.jsonpath.PathNotFoundException; | |
-import com.jayway.jsonpath.internal.PathRef; | |
-import com.jayway.jsonpath.internal.Utils; | |
-import com.jayway.jsonpath.internal.function.PathFunction; | |
-import com.jayway.jsonpath.spi.json.JsonProvider; | |
- | |
-import java.util.List; | |
- | |
-public abstract class PathToken { | |
+package org.embulk.filter.column.path; | |
+public abstract class PathToken | |
+{ | |
private PathToken prev; | |
private PathToken next; | |
- private Boolean definite = null; | |
- private Boolean upstreamDefinite = null; | |
- PathToken appendTailToken(PathToken next) { | |
+ PathToken appendTailToken(PathToken next) | |
+ { | |
this.next = next; | |
this.next.prev = this; | |
return next; | |
} | |
- void handleObjectProperty(String currentPath, Object model, EvaluationContextImpl ctx, List<String> properties) { | |
- | |
- if(properties.size() == 1) { | |
- String property = properties.get(0); | |
- String evalPath = Utils.concat(currentPath, "['", property, "']"); | |
- Object propertyVal = readObjectProperty(property, model, ctx); | |
- if(propertyVal == JsonProvider.UNDEFINED){ | |
- // Conditions below heavily depend on current token type (and its logic) and are not "universal", | |
- // so this code is quite dangerous (I'd rather rewrite it & move to PropertyPathToken and implemented | |
- // WildcardPathToken as a dynamic multi prop case of PropertyPathToken). | |
- // Better safe than sorry. | |
- assert this instanceof PropertyPathToken : "only PropertyPathToken is supported"; | |
- | |
- if(isLeaf()) { | |
- if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)){ | |
- propertyVal = null; | |
- } else { | |
- if(ctx.options().contains(Option.SUPPRESS_EXCEPTIONS) || | |
- !ctx.options().contains(Option.REQUIRE_PROPERTIES)){ | |
- return; | |
- } else { | |
- throw new PathNotFoundException("No results for path: " + evalPath); | |
- } | |
- } | |
- } else { | |
- if (! (isUpstreamDefinite() && isTokenDefinite()) && | |
- !ctx.options().contains(Option.REQUIRE_PROPERTIES) || | |
- ctx.options().contains(Option.SUPPRESS_EXCEPTIONS)){ | |
- // If there is some indefiniteness in the path and properties are not required - we'll ignore | |
- // absent property. And also in case of exception suppression - so that other path evaluation | |
- // branches could be examined. | |
- return; | |
- } else { | |
- throw new PathNotFoundException("Missing property in path " + evalPath); | |
- } | |
- } | |
- } | |
- PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, property) : PathRef.NO_OP; | |
- if (isLeaf()) { | |
- ctx.addResult(evalPath, pathRef, propertyVal); | |
- } | |
- else { | |
- next().evaluate(evalPath, pathRef, propertyVal, ctx); | |
- } | |
- } else { | |
- String evalPath = currentPath + "[" + Utils.join(", ", "'", properties) + "]"; | |
- | |
- assert isLeaf() : "non-leaf multi props handled elsewhere"; | |
- | |
- Object merged = ctx.jsonProvider().createMap(); | |
- for (String property : properties) { | |
- Object propertyVal; | |
- if(hasProperty(property, model, ctx)) { | |
- propertyVal = readObjectProperty(property, model, ctx); | |
- if(propertyVal == JsonProvider.UNDEFINED){ | |
- if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)) { | |
- propertyVal = null; | |
- } else { | |
- continue; | |
- } | |
- } | |
- } else { | |
- if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)){ | |
- propertyVal = null; | |
- } else if (ctx.options().contains(Option.REQUIRE_PROPERTIES)) { | |
- throw new PathNotFoundException("Missing property in path " + evalPath); | |
- } else { | |
- continue; | |
- } | |
- } | |
- ctx.jsonProvider().setProperty(merged, property, propertyVal); | |
- } | |
- PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, properties) : PathRef.NO_OP; | |
- ctx.addResult(evalPath, pathRef, merged); | |
- } | |
- } | |
- | |
- private static boolean hasProperty(String property, Object model, EvaluationContextImpl ctx) { | |
- return ctx.jsonProvider().getPropertyKeys(model).contains(property); | |
- } | |
- | |
- private static Object readObjectProperty(String property, Object model, EvaluationContextImpl ctx) { | |
- return ctx.jsonProvider().getMapValue(model, property); | |
- } | |
- | |
- | |
- protected void handleArrayIndex(int index, String currentPath, Object model, EvaluationContextImpl ctx) { | |
- String evalPath = Utils.concat(currentPath, "[", String.valueOf(index), "]"); | |
- PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, index) : PathRef.NO_OP; | |
- try { | |
- Object evalHit = ctx.jsonProvider().getArrayIndex(model, index); | |
- if (isLeaf()) { | |
- ctx.addResult(evalPath, pathRef, evalHit); | |
- } else { | |
- next().evaluate(evalPath, pathRef, evalHit, ctx); | |
- } | |
- } catch (IndexOutOfBoundsException e) { | |
- } | |
- } | |
- | |
- PathToken prev(){ | |
+ public PathToken prev() | |
+ { | |
return prev; | |
} | |
- PathToken next() { | |
+ public PathToken next() | |
+ { | |
if (isLeaf()) { | |
throw new IllegalStateException("Current path token is a leaf"); | |
} | |
return next; | |
} | |
- boolean isLeaf() { | |
+ boolean isLeaf() | |
+ { | |
return next == null; | |
} | |
- boolean isRoot() { | |
- return prev == null; | |
- } | |
- | |
- boolean isUpstreamDefinite() { | |
- if (upstreamDefinite == null) { | |
- upstreamDefinite = isRoot() || prev.isTokenDefinite() && prev.isUpstreamDefinite(); | |
- } | |
- return upstreamDefinite; | |
+ boolean isRoot() | |
+ { | |
+ return prev == null; | |
} | |
- public int getTokenCount() { | |
- int cnt = 1; | |
- PathToken token = this; | |
- | |
- while (!token.isLeaf()){ | |
- token = token.next(); | |
- cnt++; | |
- } | |
- return cnt; | |
- } | |
- | |
- public boolean isPathDefinite() { | |
- if(definite != null){ | |
- return definite.booleanValue(); | |
- } | |
- boolean isDefinite = isTokenDefinite(); | |
- if (isDefinite && !isLeaf()) { | |
- isDefinite = next.isPathDefinite(); | |
- } | |
- definite = isDefinite; | |
- return isDefinite; | |
- } | |
+ public abstract String getPathFragment(); | |
@Override | |
- public String toString() { | |
+ public String toString() | |
+ { | |
if (isLeaf()) { | |
return getPathFragment(); | |
} else { | |
return getPathFragment() + next().toString(); | |
} | |
} | |
- | |
- @Override | |
- public int hashCode() { | |
- return toString().hashCode(); | |
- } | |
- | |
- @Override | |
- public boolean equals(Object obj) { | |
- return super.equals(obj); | |
- } | |
- | |
- public void invoke(PathFunction pathFunction, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { | |
- ctx.addResult(currentPath, parent, pathFunction.invoke(currentPath, parent, model, ctx, null)); | |
- } | |
- | |
- public abstract void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx); | |
- | |
- public abstract boolean isTokenDefinite(); | |
- | |
- protected abstract String getPathFragment(); | |
- | |
} | |
diff -u ./json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenAppender.java ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/PathTokenAppender.java | |
--- ./json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenAppender.java 2016-08-03 17:14:02.000000000 +0900 | |
+++ ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/PathTokenAppender.java 2016-08-24 14:33:21.000000000 +0900 | |
@@ -1,5 +1,6 @@ | |
-package com.jayway.jsonpath.internal.path; | |
+package org.embulk.filter.column.path; | |
-public interface PathTokenAppender { | |
+public interface PathTokenAppender | |
+{ | |
PathTokenAppender appendPathToken(PathToken next); | |
} | |
diff -u ./json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenFactory.java ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/PathTokenFactory.java | |
--- ./json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenFactory.java 2016-08-03 17:14:02.000000000 +0900 | |
+++ ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/PathTokenFactory.java 2016-08-24 14:33:21.000000000 +0900 | |
@@ -1,52 +1,26 @@ | |
-package com.jayway.jsonpath.internal.path; | |
+package org.embulk.filter.column.path; | |
-import com.jayway.jsonpath.Predicate; | |
-import com.jayway.jsonpath.internal.function.Parameter; | |
+public class PathTokenFactory | |
+{ | |
-import java.util.Collection; | |
-import java.util.List; | |
- | |
-import static java.util.Collections.singletonList; | |
- | |
-public class PathTokenFactory { | |
- | |
- public static RootPathToken createRootPathToken(char token) { | |
- return new RootPathToken(token); | |
- } | |
- | |
- public static PathToken createSinglePropertyPathToken(String property, char stringDelimiter) { | |
- return new PropertyPathToken(singletonList(property), stringDelimiter); | |
- } | |
- | |
- public static PathToken createPropertyPathToken(List<String> properties, char stringDelimiter) { | |
- return new PropertyPathToken(properties, stringDelimiter); | |
+ public static CompiledPath createRootPathToken(char token) | |
+ { | |
+ return new CompiledPath(token); | |
} | |
- public static PathToken createSliceArrayPathToken(final ArraySliceOperation arraySliceOperation) { | |
- return new ArrayPathToken(arraySliceOperation); | |
+ public static PathToken createPropertyPathToken(String property, char stringDelimiter) | |
+ { | |
+ return new PropertyPathToken(property, stringDelimiter); | |
} | |
- public static PathToken createIndexArrayPathToken(final ArrayIndexOperation arrayIndexOperation) { | |
- return new ArrayPathToken(arrayIndexOperation); | |
+ public static PathToken createIndexArrayPathToken(final Integer index) | |
+ { | |
+ return new ArrayPathToken(index); | |
} | |
- public static PathToken createWildCardPathToken() { | |
+ public static PathToken createWildCardPathToken() | |
+ { | |
return new WildcardPathToken(); | |
} | |
- public static PathToken crateScanToken() { | |
- return new ScanPathToken(); | |
- } | |
- | |
- public static PathToken createPredicatePathToken(Collection<Predicate> predicates) { | |
- return new PredicatePathToken(predicates); | |
- } | |
- | |
- public static PathToken createPredicatePathToken(Predicate predicate) { | |
- return new PredicatePathToken(predicate); | |
- } | |
- | |
- public static PathToken createFunctionPathToken(String function, List<Parameter> parameters) { | |
- return new FunctionPathToken(function, parameters); | |
- } | |
} | |
Only in ./json-path/src/main/java/com/jayway/jsonpath/internal/path/: PredicateContextImpl.java | |
Only in ./json-path/src/main/java/com/jayway/jsonpath/internal/path/: PredicatePathToken.java | |
diff -u ./json-path/src/main/java/com/jayway/jsonpath/internal/path/PropertyPathToken.java ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/PropertyPathToken.java | |
--- ./json-path/src/main/java/com/jayway/jsonpath/internal/path/PropertyPathToken.java 2016-08-03 17:14:02.000000000 +0900 | |
+++ ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/PropertyPathToken.java 2016-08-24 14:33:21.000000000 +0900 | |
@@ -1,105 +1,30 @@ | |
-/* | |
- * Copyright 2011 the original author or authors. | |
- * Licensed under the Apache License, Version 2.0 (the "License"); | |
- * you may not use this file except in compliance with the License. | |
- * You may obtain a copy of the License at | |
- * | |
- * http://www.apache.org/licenses/LICENSE-2.0 | |
- * | |
- * Unless required by applicable law or agreed to in writing, software | |
- * distributed under the License is distributed on an "AS IS" BASIS, | |
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
- * See the License for the specific language governing permissions and | |
- * limitations under the License. | |
- */ | |
-package com.jayway.jsonpath.internal.path; | |
+package org.embulk.filter.column.path; | |
-import com.jayway.jsonpath.InvalidPathException; | |
-import com.jayway.jsonpath.PathNotFoundException; | |
-import com.jayway.jsonpath.internal.PathRef; | |
-import com.jayway.jsonpath.internal.Utils; | |
+public class PropertyPathToken extends PathToken | |
+{ | |
-import java.util.ArrayList; | |
-import java.util.List; | |
- | |
-import static com.jayway.jsonpath.internal.Utils.onlyOneIsTrueNonThrow; | |
- | |
-/** | |
- * | |
- */ | |
-class PropertyPathToken extends PathToken { | |
- | |
- private final List<String> properties; | |
+ private final String property; | |
private final String stringDelimiter; | |
- public PropertyPathToken(List<String> properties, char stringDelimiter) { | |
- if (properties.isEmpty()) { | |
- throw new InvalidPathException("Empty properties"); | |
+ public PropertyPathToken(String property, char stringDelimiter) | |
+ { | |
+ if (property.isEmpty()) { | |
+ throw new InvalidPathException("Empty property"); | |
} | |
- this.properties = properties; | |
+ this.property = property; | |
this.stringDelimiter = Character.toString(stringDelimiter); | |
} | |
- public List<String> getProperties() { | |
- return properties; | |
- } | |
- | |
- public boolean singlePropertyCase() { | |
- return properties.size() == 1; | |
- } | |
- | |
- public boolean multiPropertyMergeCase() { | |
- return isLeaf() && properties.size() > 1; | |
- } | |
- | |
- public boolean multiPropertyIterationCase() { | |
- // Semantics of this case is the same as semantics of ArrayPathToken with INDEX_SEQUENCE operation. | |
- return ! isLeaf() && properties.size() > 1; | |
- } | |
- | |
- @Override | |
- public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { | |
- // Can't assert it in ctor because isLeaf() could be changed later on. | |
- assert onlyOneIsTrueNonThrow(singlePropertyCase(), multiPropertyMergeCase(), multiPropertyIterationCase()); | |
- | |
- if (!ctx.jsonProvider().isMap(model)) { | |
- if (! isUpstreamDefinite()) { | |
- return; | |
- } else { | |
- String m = model == null ? "null" : model.getClass().getName(); | |
- | |
- throw new PathNotFoundException(String.format( | |
- "Expected to find an object with property %s in path %s but found '%s'. " + | |
- "This is not a json object according to the JsonProvider: '%s'.", | |
- getPathFragment(), currentPath, m, ctx.configuration().jsonProvider().getClass().getName())); | |
- } | |
- } | |
- | |
- if (singlePropertyCase() || multiPropertyMergeCase()) { | |
- handleObjectProperty(currentPath, model, ctx, properties); | |
- return; | |
- } | |
- | |
- assert multiPropertyIterationCase(); | |
- final List<String> currentlyHandledProperty = new ArrayList<String>(1); | |
- currentlyHandledProperty.add(null); | |
- for (final String property : properties) { | |
- currentlyHandledProperty.set(0, property); | |
- handleObjectProperty(currentPath, model, ctx, currentlyHandledProperty); | |
- } | |
- } | |
- | |
- @Override | |
- public boolean isTokenDefinite() { | |
- // in case of leaf multiprops will be merged, so it's kinda definite | |
- return singlePropertyCase() || multiPropertyMergeCase(); | |
- } | |
+ public String getProperty() { return property; } | |
@Override | |
- public String getPathFragment() { | |
+ public String getPathFragment() | |
+ { | |
return new StringBuilder() | |
.append("[") | |
- .append(Utils.join(",", stringDelimiter, properties)) | |
+ .append(stringDelimiter) | |
+ .append(property) | |
+ .append(stringDelimiter) | |
.append("]").toString(); | |
} | |
} | |
Only in ./json-path/src/main/java/com/jayway/jsonpath/internal/path/: RootPathToken.java | |
Only in ./json-path/src/main/java/com/jayway/jsonpath/internal/path/: ScanPathToken.java | |
diff -u ./json-path/src/main/java/com/jayway/jsonpath/internal/path/WildcardPathToken.java ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/WildcardPathToken.java | |
--- ./json-path/src/main/java/com/jayway/jsonpath/internal/path/WildcardPathToken.java 2016-08-03 17:14:02.000000000 +0900 | |
+++ ~/src/sonots/embulk-filter-column/src/main/java/org/embulk/filter/column/path/WildcardPathToken.java 2016-08-24 14:33:21.000000000 +0900 | |
@@ -1,60 +1,14 @@ | |
-/* | |
- * Copyright 2011 the original author or authors. | |
- * Licensed under the Apache License, Version 2.0 (the "License"); | |
- * you may not use this file except in compliance with the License. | |
- * You may obtain a copy of the License at | |
- * | |
- * http://www.apache.org/licenses/LICENSE-2.0 | |
- * | |
- * Unless required by applicable law or agreed to in writing, software | |
- * distributed under the License is distributed on an "AS IS" BASIS, | |
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
- * See the License for the specific language governing permissions and | |
- * limitations under the License. | |
- */ | |
-package com.jayway.jsonpath.internal.path; | |
+package org.embulk.filter.column.path; | |
-import com.jayway.jsonpath.Option; | |
-import com.jayway.jsonpath.PathNotFoundException; | |
-import com.jayway.jsonpath.internal.PathRef; | |
- | |
-import static java.util.Arrays.asList; | |
- | |
-/** | |
- * | |
- */ | |
-public class WildcardPathToken extends PathToken { | |
- | |
- WildcardPathToken() { | |
- } | |
- | |
- @Override | |
- public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { | |
- if (ctx.jsonProvider().isMap(model)) { | |
- for (String property : ctx.jsonProvider().getPropertyKeys(model)) { | |
- handleObjectProperty(currentPath, model, ctx, asList(property)); | |
- } | |
- } else if (ctx.jsonProvider().isArray(model)) { | |
- for (int idx = 0; idx < ctx.jsonProvider().length(model); idx++) { | |
- try { | |
- handleArrayIndex(idx, currentPath, model, ctx); | |
- } catch (PathNotFoundException p){ | |
- if(ctx.options().contains(Option.REQUIRE_PROPERTIES)){ | |
- throw p; | |
- } | |
- } | |
- } | |
- } | |
- } | |
- | |
- | |
- @Override | |
- public boolean isTokenDefinite() { | |
- return false; | |
+public class WildcardPathToken extends PathToken | |
+{ | |
+ WildcardPathToken() | |
+ { | |
} | |
@Override | |
- public String getPathFragment() { | |
+ public String getPathFragment() | |
+ { | |
return "[*]"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
unescape from jsonpath/internal/Utils https://github.com/jayway/JsonPath/blob/c2c1686139a128c08578cab377376ffb4b73c623/json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java#L172-L245