Skip to content

Expression parsing

michal-kapala edited this page Nov 30, 2023 · 3 revisions

jitterbit-studio-core

This library parses script expressions, resolving dependencies on external data elements (e.g. sources, targets, transformations).

See Transform.Token for tokenization details.

ExpressionParser

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;
        }
    }
}

DataElementExtractor

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);
        }
    }
}
Clone this wiki locally