io.cdap.wrangler.parser.MapArguments Maven / Gradle / Ivy
/*
* Copyright © 2017-2019 Cask Data, Inc.
*
* 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 io.cdap.wrangler.parser;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.cdap.wrangler.api.Arguments;
import io.cdap.wrangler.api.DirectiveParseException;
import io.cdap.wrangler.api.LazyNumber;
import io.cdap.wrangler.api.TokenGroup;
import io.cdap.wrangler.api.parser.Bool;
import io.cdap.wrangler.api.parser.BoolList;
import io.cdap.wrangler.api.parser.ColumnName;
import io.cdap.wrangler.api.parser.ColumnNameList;
import io.cdap.wrangler.api.parser.Numeric;
import io.cdap.wrangler.api.parser.NumericList;
import io.cdap.wrangler.api.parser.Text;
import io.cdap.wrangler.api.parser.TextList;
import io.cdap.wrangler.api.parser.Token;
import io.cdap.wrangler.api.parser.TokenDefinition;
import io.cdap.wrangler.api.parser.TokenType;
import io.cdap.wrangler.api.parser.UsageDefinition;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Class description here.
*/
public class MapArguments implements Arguments {
private final Map tokens;
private final int lineno;
private final int columnno;
private final String source;
public MapArguments(UsageDefinition definition, TokenGroup group) throws DirectiveParseException {
this.tokens = new HashMap<>();
this.lineno = group.getSourceInfo().getLineNumber();
this.columnno = group.getSourceInfo().getColumnNumber();
this.source = group.getSourceInfo().getSource();
int required = definition.getTokens().size() - definition.getOptionalTokensCount();
if ((required > group.size() - 1) || ((group.size() - 1) > definition.getTokens().size())) {
throw new DirectiveParseException(
definition.getDirectiveName(), String.format("Improper usage of directive '%s', usage - '%s'",
definition.getDirectiveName(), definition.toString()));
}
List specifications = definition.getTokens();
Iterator it = group.iterator();
int pos = 0;
it.next(); // skip directive name.
while (it.hasNext()) {
Token token = it.next();
while (pos < specifications.size()) {
TokenDefinition specification = specifications.get(pos);
if (!specification.optional()) {
if (!specification.type().equals(token.type())) {
if (specification.type() == TokenType.COLUMN_NAME_LIST && token.type() == TokenType.COLUMN_NAME) {
List values = new ArrayList<>();
values.add(((ColumnName) token).value());
tokens.put(specification.name(), new ColumnNameList(values));
pos = pos + 1;
break;
} else if (specification.type() == TokenType.NUMERIC_LIST && token.type() == TokenType.NUMERIC) {
List values = new ArrayList<>();
values.add(((Numeric) token).value());
tokens.put(specification.name(), new NumericList(values));
pos = pos + 1;
break;
} else if (specification.type() == TokenType.BOOLEAN_LIST && token.type() == TokenType.BOOLEAN) {
List values = new ArrayList<>();
values.add(((Bool) token).value());
tokens.put(specification.name(), new BoolList(values));
pos = pos + 1;
break;
} else if (specification.type() == TokenType.TEXT_LIST && token.type() == TokenType.TEXT) {
List values = new ArrayList<>();
values.add(((Text) token).value());
tokens.put(specification.name(), new TextList(values));
pos = pos + 1;
break;
} else {
throw new DirectiveParseException(
String.format("Expected argument '%s' to be of type '%s', but it is of type '%s' - %s",
specification.name(), specification.type().name(),
token.type().name(), group.getSourceInfo().toString())
);
}
} else {
tokens.put(specification.name(), token);
pos = pos + 1;
break;
}
} else {
pos = pos + 1;
if (specification.type().equals(token.type())) {
tokens.put(specification.name(), token);
break;
}
}
}
}
}
/**
* Returns the number of tokens that are mapped to arguments.
*
* The optional arguments specified during the UsageDefinition
* are not included in the size if they are not present in the tokens parsed.
*
* @return number of tokens parsed, excluding optional tokens if not present.
*/
@Override
public int size() {
return tokens.size();
}
/**
* This method checks if there exists a token named name
registered
* with this object.
*
* The name
is expected to the same as specified in the UsageDefinition
.
* There are two reason why the name
might not exists in this object :
*
*
* - When an token is defined to be optional, the user might not have specified the
* token, hence the token would not exist in the argument.
* - User has specified invalid
name
.
*
*
* @param name associated with the token.
* @return true if argument with name name
exists, false otherwise.
*/
@Override
public boolean contains(String name) {
return tokens.containsKey(name);
}
/**
* This method returns the token {@code value} based on the {@code name}
* specified in the argument. This method will attempt to convert the token
* into the expected return type T
.
*
*
If the name
doesn't exist in this object, then this
* method is expected to return null
*
* @param name of the token to be retrieved.
* @return object that extends Token
.
*/
@Override
public T value(String name) {
return (T) tokens.get(name);
}
/**
* Each token is defined as one of the types defined in the class {@link TokenType}.
* When the directive is parsed into token, the type of the token is passed through.
*
* @param name associated with the token.
* @return TokenType
associated with argument name
, else null.
*/
@Override
public TokenType type(String name) {
return tokens.get(name).type();
}
/**
* Returns the source line number these arguments were parsed from.
*
* @return the source line number.
*/
@Override
public int line() {
return lineno;
}
/**
* Returns the source column number these arguments were parsed from.
* It takes the start position of the directive as the column number.
*
* @return the start of the column number for the start of the directive
* these arguments contain.
*/
@Override
public int column() {
return columnno;
}
/**
* This method returns the original source line of the directive as specified
* the user. It returns the String
representation of the directive.
*
* @return String
object representing the original directive
* as specified by the user.
*/
@Override
public String source() {
return source;
}
/**
* Returns JsonElement
representation of this object.
*
* @return an instance of JsonElement
object representing all the
* named tokens held within this object.
*/
@Override
public JsonElement toJson() {
JsonObject object = new JsonObject();
JsonObject arguments = new JsonObject();
for (Map.Entry entry : tokens.entrySet()) {
arguments.add(entry.getKey(), entry.getValue().toJson());
}
object.addProperty("line", lineno);
object.addProperty("column", columnno);
object.addProperty("source", source);
object.add("arguments", arguments);
return object;
}
}