-
Notifications
You must be signed in to change notification settings - Fork 0
Expression parsing
michal-kapala edited this page Nov 30, 2023
·
3 revisions
This library parses script expressions, resolving dependencies on external data elements (e.g. sources, targets, transformations).
See Transform.Token
for tokenization details.
jitterbit-studio-core-1.0.0-SNAPSHOT\org\jitterbit\integration\data\script\ExpressionParser.java
package org.jitterbit.integration.data.script;
import javax.annotation.Nullable;
import java.util.List;
import com.google.common.collect.ImmutableSortedSet;
import org.jitterbit.integration.data.script.de.DataElementReference;
import com.google.common.collect.ImmutableSet;
import com.google.common.base.Preconditions;
import java.util.ArrayList;
import org.jitterbit.integration.data.script.de.ReferenceLocation;
public final class ExpressionParser
{
private final ReferenceLocation refLocation;
private final Transform transform;
private final ScriptValidator validator;
private boolean sourceDataElementAware;
private ArrayList<JavaScriptError> JSerrors;
public ArrayList<JavaScriptError> getJSerrors() {
return this.JSerrors;
}
public ExpressionParser(final ExpressionSource source) {
this(source.asReferenceLocation());
}
public ExpressionParser(final ReferenceLocation refLocation) {
this.JSerrors = new ArrayList<JavaScriptError>();
this.refLocation = (ReferenceLocation)Preconditions.checkNotNull((Object)refLocation);
this.transform = new Transform();
this.validator = new ScriptValidator();
}
public ReferenceLocation getReferenceLocation() {
return this.refLocation;
}
public void setSourceDataElementContainer(final SourceDataElementContainer sourceDEs) {
this.validator.setSourceDataElementContainer(sourceDEs);
this.transform.setSourceDataElementContainer(sourceDEs);
this.sourceDataElementAware = true;
}
public Result parse(final String expression) {
return this.parse(expression, false);
}
public Result parse(final String expression, final boolean isJavaScript) {
if (isJavaScript) {
final JavaScriptParser JSParser = new JavaScriptParser();
this.JSerrors = JSParser.getErrors();
return JSParser.parse(expression, this.refLocation);
}
final List<Transform.Token> tokens = this.transform.tokenize(expression);
if (tokens == null) {
return new Result(this.createInvalidStatus(), null, null, null);
}
final DataElementExtractor deExtractor = new DataElementExtractor();
deExtractor.setSourceDataElementAware(this.sourceDataElementAware);
deExtractor.parse(tokens);
final ScriptStatus status = this.validate();
return new Result(status, deExtractor.getGlobalDataElementReferences(this.refLocation), deExtractor.getLocalDataElements(), deExtractor.getSourceDataElements());
}
private ScriptStatus createInvalidStatus() {
return ScriptStatus.invalid((this.transform.m_err != null) ? this.transform.m_err.getFormattedErrorMessage() : "Unknown error");
}
private ScriptStatus validate() {
final List<Transform.Token> rpnOrdered = this.transform.rpnOrder();
return (rpnOrdered != null) ? this.validator.validate(rpnOrdered) : this.createInvalidStatus();
}
@Nullable
public ImmutableSet<DataElementReference> extractDataElementReferences(final String expression) {
final List<Transform.Token> tokens = this.transform.tokenize(expression);
return (tokens != null) ? this.extractDataElementReferences(tokens) : null;
}
public ImmutableSet<DataElementReference> extractDataElementReferences(final List<Transform.Token> tokens) {
final DataElementExtractor extractor = new DataElementExtractor();
extractor.parse(tokens);
return extractor.getGlobalDataElementReferences(this.refLocation);
}
public static final class Result
{
private final ScriptStatus status;
private final ImmutableSet<DataElementReference> globalDeRefs;
private final ImmutableSortedSet<String> localDEs;
private final ImmutableSortedSet<String> sourceDEs;
public Result(final ScriptStatus status, final ImmutableSet<DataElementReference> deRefs, final ImmutableSortedSet<String> localDEs, final ImmutableSortedSet<String> sourceDEs) {
this.status = status;
this.globalDeRefs = deRefs;
this.localDEs = localDEs;
this.sourceDEs = sourceDEs;
}
public Result(final ScriptStatus status) {
this.status = status;
this.globalDeRefs = null;
this.localDEs = null;
this.sourceDEs = null;
}
public ScriptStatus getStatus() {
return this.status;
}
@Nullable
public ImmutableSet<DataElementReference> getGlobalDataElementReferences() {
return this.globalDeRefs;
}
@Nullable
public ImmutableSet<String> getSourceDataElements() {
return (ImmutableSet<String>)this.sourceDEs;
}
@Nullable
public ImmutableSet<String> getLocalDataElements() {
return (ImmutableSet<String>)this.localDEs;
}
}
}
jitterbit-studio-core-1.0.0-SNAPSHOT\org\jitterbit\integration\data\script\DataElementExtractor.java
package org.jitterbit.integration.data.script;
import org.jitterbit.lang.KongaStringUtils;
import org.jitterbit.integration.dataelement.DeSyntax;
import java.util.Collection;
import com.google.common.collect.ImmutableSortedSet;
import java.util.Iterator;
import org.jitterbit.integration.data.script.de.DataElementReference;
import com.google.common.collect.ImmutableSet;
import org.jitterbit.integration.data.script.de.ReferenceLocation;
import java.util.List;
import java.util.Comparator;
import java.util.TreeSet;
import java.util.HashMap;
import java.util.SortedSet;
import org.jitterbit.integration.data.script.de.AccessType;
import java.util.EnumSet;
import java.util.Map;
final class DataElementExtractor
{
private final Map<String, EnumSet<AccessType>> globalDEs;
private final SortedSet<String> sourceDEs;
private final SortedSet<String> localDEs;
private boolean sourceDataElementAware;
DataElementExtractor() {
this.globalDEs = new HashMap<String, EnumSet<AccessType>>();
this.sourceDEs = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
this.localDEs = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
}
public void setSourceDataElementAware(final boolean value) {
this.sourceDataElementAware = value;
}
public void parse(final List<Transform.Token> tokens) {
final Parser parser = new Parser();
parser.parse(tokens);
}
public ImmutableSet<DataElementReference> getGlobalDataElementReferences(final ReferenceLocation refLocation) {
final ImmutableSet.Builder<DataElementReference> refs = (ImmutableSet.Builder<DataElementReference>)ImmutableSet.builder();
for (final Map.Entry<String, EnumSet<AccessType>> e : this.globalDEs.entrySet()) {
refs.add((Object)new DataElementReference(e.getKey(), refLocation, e.getValue()));
}
return (ImmutableSet<DataElementReference>)refs.build();
}
public ImmutableSortedSet<String> getSourceDataElements() {
return (ImmutableSortedSet<String>)ImmutableSortedSet.copyOf((Collection)this.sourceDEs);
}
public ImmutableSortedSet<String> getLocalDataElements() {
return (ImmutableSortedSet<String>)ImmutableSortedSet.copyOf((Collection)this.localDEs);
}
private final class Parser
{
private boolean isRead;
private int index;
private List<Transform.Token> tokens;
public void parse(final List<Transform.Token> tokens) {
this.tokens = tokens;
for (final Transform.Token t : tokens) {
this.processToken(t);
++this.index;
}
}
private void processToken(final Transform.Token t) {
switch (t.m_id) {
case 406: {
this.processGlobalDataElement(t);
break;
}
case 101: {
this.checkSetAndGet(t);
break;
}
case 405: {
this.processSourceDataElement(t);
break;
}
case 408: {
this.processLocalDataElement(t);
break;
}
case 320: {
this.isRead = true;
break;
}
case 1100:
case 1101: {
break;
}
default: {
this.isRead = false;
break;
}
}
}
private void processGlobalDataElement(final Transform.Token t) {
if (t.m_str.length() > 1) {
final AccessType type = this.getAccessType();
final String name = DeSyntax.SCRIPT.strip(t.m_str);
this.addAccess(name, type);
}
}
private AccessType getAccessType() {
AccessType type = AccessType.UNKNOWN;
if (this.isRead) {
type = AccessType.GET;
}
else {
type = (this.isAssignment(this.tokens, this.index + 1) ? AccessType.SET : AccessType.GET);
}
return type;
}
private boolean isAssignment(final List<Transform.Token> tokens, final int current) {
int bracketCount = 0;
for (int index = current, size = tokens.size(); index < size; ++index) {
final Transform.Token t = tokens.get(index);
switch (t.m_id) {
case 320: {
return true;
}
case 1100:
case 1101: {
break;
}
case 1004: {
++bracketCount;
break;
}
case 1005: {
--bracketCount;
break;
}
default: {
if (bracketCount <= 0) {
return false;
}
break;
}
}
}
return false;
}
private void checkSetAndGet(final Transform.Token t) {
if ("set(".equalsIgnoreCase(t.m_str) || "get(".equalsIgnoreCase(t.m_str)) {
final String name = this.getFirstStringArgument(this.tokens, this.index + 1);
if (name != null) {
final AccessType type = "set(".equalsIgnoreCase(t.m_str) ? AccessType.SET : AccessType.GET;
this.addAccess(name, type);
}
}
}
private void processSourceDataElement(final Transform.Token t) {
if (DataElementExtractor.this.sourceDataElementAware) {
DataElementExtractor.this.sourceDEs.add(t.m_str);
}
this.isRead = false;
}
private void processLocalDataElement(final Transform.Token t) {
DataElementExtractor.this.localDEs.add(t.m_str);
this.isRead = false;
}
private void addAccess(final String de, final AccessType type) {
EnumSet<AccessType> access = DataElementExtractor.this.globalDEs.get(de);
if (access == null) {
access = ((type != null) ? EnumSet.of(type) : EnumSet.noneOf(AccessType.class));
DataElementExtractor.this.globalDEs.put(de, access);
}
else if (type != null) {
access.add(type);
}
}
private String getFirstStringArgument(final List<Transform.Token> tokens, final int current) {
String name = null;
for (int index = current, size = tokens.size(); index < size; ++index) {
final Transform.Token t = tokens.get(index);
switch (t.m_id) {
case 401: {
name = this.removeBeginAndEndQuotes(t);
break;
}
case 1100:
case 1101: {
break;
}
case 200:
case 1000: {
return name;
}
default: {
return null;
}
}
}
return null;
}
private String removeBeginAndEndQuotes(final Transform.Token t) {
return KongaStringUtils.unwrap(t.m_str);
}
}
}