Skip to content

Commit

Permalink
Add name index for TDNode for large number of chilrens; Improve CliArg
Browse files Browse the repository at this point in the history
  • Loading branch information
jianwu committed Aug 8, 2023
1 parent 49fd4b6 commit 1e57827
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 36 deletions.
33 changes: 20 additions & 13 deletions CliArg/src/main/java/org/jsonex/cliarg/CLIParser.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
package org.jsonex.cliarg;

import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.jsonex.core.util.BeanConvertContext;
import org.jsonex.core.util.ClassUtil;
import static org.jsonex.core.util.LangUtil.doIf;
import static org.jsonex.core.util.LangUtil.doIfNotNull;
import static org.jsonex.core.util.ListUtil.isIn;
import org.jsonex.jsoncoder.JSONCoder;
import org.jsonex.jsoncoder.JSONCoderOption;
import org.jsonex.treedoc.TDNode.Type;
import org.jsonex.treedoc.json.TDJSONOption;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

import static org.jsonex.core.util.LangUtil.doIf;
import static org.jsonex.core.util.ListUtil.isIn;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Parse the input command line arguments against the {@link CLISpec}. The parsed result will be stored in the target
Expand Down Expand Up @@ -106,7 +112,7 @@ private void parseArg(String arg) {
}
Param param = spec.indexedParams.get(paramIndex++);
missingParams.remove(param.name);
param.property.set(target, parseValue(param, arg));
doIfNotNull(parseValue(param, arg), v -> param.property.set(target, v));
}

private Object parseValue(Param param, String value) {
Expand All @@ -124,7 +130,8 @@ private Object parseValue(Param param, String value) {
? JSONCoder.decode(value, cls, opt)
: JSONCoder.decodeTo(value, param.getProperty().get(target), opt.setMergeArray(true));
} catch (Exception e) {
log.error("Error parsing parameter:" + param.name, e);
errorMessages.put(param.name, value + ";" + e.toString());
// log.error("Error parsing parameter:" + param.name, e);
}
return null;
}
Expand All @@ -133,9 +140,9 @@ private Object parseValue(Param param, String value) {

public String getErrorsAsString() {
StringBuilder sb = new StringBuilder();
doIf(!missingParams.isEmpty(), () -> sb.append("\nMissing required arguments:" + missingParams));
doIf(!extraArgs.isEmpty(), () -> sb.append("\nUnexpected arguments:" + extraArgs));
doIf(!errorMessages.isEmpty(), () -> sb.append("\nError parsing following arguments:" + errorMessages));
doIf(!missingParams.isEmpty(), () -> sb.append("\nMissing required arguments:").append(missingParams));
doIf(!extraArgs.isEmpty(), () -> sb.append("\nUnexpected arguments:").append(extraArgs));
doIf(!errorMessages.isEmpty(), () -> sb.append("\nError parsing following arguments:").append(errorMessages));
return sb.toString();
}
}
4 changes: 3 additions & 1 deletion CliArg/src/main/java/org/jsonex/cliarg/CLISpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@
import static org.jsonex.core.util.ListUtil.setAt;

/**
* <pre>
* CLI specification based on annotated java bean of `cls`. Following annotations will be processed:
*
* Class level:
* {@link Name}: Name of the command. (Optional) Default to the class simple name
* {@link Summary}: Summary of the command (Optional)
* {@link Description}: Description of the command (Optional)
* {@link Examples}: Array of string representation of samples usages (Optional)
*
* For field level annotations, please refer to class {@link Param}
*
* </pre>
*
* @param <T>
*/
@Data
Expand Down
23 changes: 12 additions & 11 deletions CliArg/src/main/java/org/jsonex/cliarg/Param.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,28 @@
import static org.jsonex.core.util.StringUtil.noNull;

/**
* Represent an command line parameter, it can be either argument or option
* If index is not null indicate it's argument
* Argument default to required unless explicitly specified.
* required argument can't follow non-required argument which index less than it
* If index is null indicates it's an option, option default to not required, unless specified
*
* For option of Boolean type, it will be mapped as flag, that means the value of the option can be omitted.
*
* <pre>
* Represent a command line parameter, it can be either argument or option
* If index is not null indicate it is an argument.
* Argument default to required unless explicitly specified.
* Required argument can't follow non-required argument which index less than it
* If index is null indicates it's an option, option default to not required, unless specified
* For option of Boolean type, it will be mapped as flag, that means the value of the option can be omitted.
* For Param of complex type or array/list, the value can be specified as JSON(ex) string, the top level "{" or "[",
* can be, omitted. The quote for key and value can be omitted.
*
* For array parameters, it also possible to specify the values as separate options. The values will be merged
*
* For array parameters, it is also possible to specify the values as separate options. The values will be merged
* Following Annotation will be processed for each parameter:
*
* {@link Name} Name of the parameter, optional, default to field name
* {@link ShortName} The optional short name
* {@link Description} The optional description
* {@link Index} Indicate this an indexed parameter
* {@link Required} Indicate if this field is required. all the index fields are required unless explicitly indicated.
* All the non-index fields are not required unless explicitly indicated.
* </pre>
*/
@Data
public class Param {
Expand Down
4 changes: 2 additions & 2 deletions CliArg/src/test/java/org/jsonex/cliarg/CliParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ public static class Arg1 {

@Test
public void testParse() {
CLISpec spec = new CLISpec(Arg1.class);
CLISpec<Arg1> spec = new CLISpec<>(Arg1.class);
assertMatchesSnapshot("spec", spec);

log.info("spec:\n" + spec.printUsage());
assertMatchesSnapshot("usage", spec.printUsage());

String[] args = { "abc", "10", "name:n1,x:1,y:2", "-o", "VAL2", "--optInt", "100",
"--arrayArg", "str1,str2,'It\\'s escapted'", "--arrayArg", "array as separate option"};
CLIParser parser = spec.parse(args, 0);
CLIParser<Arg1> parser = spec.parse(args, 0);
log.info("parsedValue:\n" + parser.target);
assertMatchesSnapshot("parserTarget", parser.target);

Expand Down
44 changes: 35 additions & 9 deletions treedoc/src/main/java/org/jsonex/treedoc/TDNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,31 @@
import lombok.experimental.Accessors;
import org.jsonex.core.charsource.Bookmark;
import org.jsonex.core.type.Lazy;
import static org.jsonex.core.util.LangUtil.orElse;
import static org.jsonex.core.util.LangUtil.safe;
import org.jsonex.core.util.ListUtil;
import static org.jsonex.core.util.ListUtil.last;
import static org.jsonex.core.util.ListUtil.listOf;
import static org.jsonex.core.util.ListUtil.map;
import org.jsonex.core.util.StringUtil;
import org.jsonex.treedoc.TDPath.Part;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;

import static org.jsonex.core.util.LangUtil.orElse;
import static org.jsonex.core.util.LangUtil.safe;
import static org.jsonex.core.util.ListUtil.*;

/** A Node in TreeDoc */
@RequiredArgsConstructor
// @Getter @Setter
@Accessors(chain = true)
public class TDNode {
public final static int SIZE_TO_INIT_NAME_INDEX = 64;
public final static String ID_KEY = "$id";
public final static String REF_KEY = "$ref";

Expand All @@ -58,6 +64,7 @@ public enum Type { MAP, ARRAY, SIMPLE }
transient private boolean deduped;
transient private final Lazy<Integer> hash = new Lazy<>();
transient private final Lazy<String> str = new Lazy<>();
transient private Map<String, Integer> nameIndex; // Will only initialize when size is big enough

public TDNode(TDNode parent, String key) { this.doc = parent.doc; this.parent = parent; this.key = key; }
public TDNode(TreeDoc doc, String key) { this.doc = doc; this.key = key; }
Expand Down Expand Up @@ -101,9 +108,20 @@ public TDNode addChild(TDNode node) {
if (node.key == null) // Assume it's array element
node.key = "" + getChildrenSize();
children.add(node);
if (children.size() > SIZE_TO_INIT_NAME_INDEX && nameIndex == null)
initNameIndex();
return touch();
}

private void initNameIndex() {
nameIndex = new HashMap<>();
for (int i = 0; i< children.size(); i++) {
TDNode child = children.get(i);
if (child.key != null)
nameIndex.put(child.key, i);
}
}

public void swapWith(TDNode to) {
if (this.parent == null || to.parent == null)
throw new IllegalArgumentException("Can't swap root node");
Expand All @@ -129,8 +147,12 @@ public TDNode getChild(String name) {
return idx < 0 ? null : children.get(idx);
}

int indexOf(TDNode node) { return ListUtil.indexOf(children, n -> n == node); }
int indexOf(String name) { return ListUtil.indexOf(children, n -> n.getKey().equals(name)); }
int indexOf(TDNode node) {
return nameIndex != null ? indexOf(node.key) : ListUtil.indexOf(children, n -> n == node);
}
int indexOf(String name) {
return nameIndex != null ? orElse(nameIndex.get(name), -1) : ListUtil.indexOf(children, n -> n.getKey().equals(name));
}
int index() { return parent == null ? 0 : parent.indexOf(this); }

public Object getChildValue(String name) {
Expand Down Expand Up @@ -206,8 +228,8 @@ public List<String> getPath() {
public boolean isLeaf() { return getChildrenSize() == 0; }

private TDNode touch() {
hash.clear();;
str.clear();;
hash.clear();
str.clear();
if (parent != null)
parent.touch();
return this;
Expand Down Expand Up @@ -285,15 +307,19 @@ public List<String> getChildrenKeys() {
if (this.type == Type.SIMPLE || children == null)
return result;
// Add the key column
Set<String> keySet = new HashSet<>();
result.add(COLUMN_KEY);
keySet.add(COLUMN_KEY);
boolean hasValue = false;
for (TDNode c : children) {
if (c.value != null)
hasValue = true;
if (c.children != null)
for (TDNode cc : c.getChildren())
if (!result.contains(cc.key))
if (!keySet.contains(cc.key)) {
result.add(cc.key);
keySet.add(cc.key);
}
}
if (hasValue)
result.add(1, COLUMN_VALUE);
Expand Down

0 comments on commit 1e57827

Please sign in to comment.