Skip to content

Updated Directives.g4,wrangler-api module, wrangler-core module #971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 11 additions & 112 deletions wrangler-api/src/main/java/io/cdap/wrangler/api/RecipeSymbol.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.cdap.wrangler.api.annotations.PublicEvolving;
import io.cdap.wrangler.api.parser.TimeDuration;
import io.cdap.wrangler.api.parser.Token;

import java.util.ArrayList;
Expand All @@ -29,36 +30,13 @@
import java.util.TreeSet;

/**
* This object <code>RecipeSymbol</code> stores information about all the
* <code>TokenGroup</code> ( TokenGroup represents a collection of tokens
* generated from parsing a single directive). The object also contains
* information about the directives (or plugins) that need to be loaded
* at the startup time.
*
* <p>This class provides some useful methods for accessing the list of
* directives or plugins that need to be loaded, the token groups for
* all the directives tokenized and parsed.</p>
*
* <p>This class exposes a builder pattern for constructing the object.
* in the <code>RecipeVisitor</code>. The <code>RecipeVisitor</code>
* constructs <code>RecipeSymbol</code> using the <code>RecipeSymbol.Builder</code></p>
* RecipeSymbol holds the parsed tokens for a recipe.
*/

@PublicEvolving
public final class RecipeSymbol {
/**
* Version if specified, else defaults to 1.0
*/
private final String version;

/**
* Set of directives or plugins that have to loaded
* during the configuration phase of <code>RecipePipeline.</code>
*/
private final Set<String> loadableDirectives;

/**
* This maintains a list of tokens for each directive parsed.
*/
private final List<TokenGroup> tokens;

private RecipeSymbol(String version, Set<String> loadableDirectives, List<TokenGroup> tokens) {
Expand All @@ -67,67 +45,26 @@ private RecipeSymbol(String version, Set<String> loadableDirectives, List<TokenG
this.tokens = tokens;
}

/**
* Returns a set of dynamically loaded directives as plugins. These are
* the set of plugins or directives that are in the recipe, but are provided
* as the user plugins.
*
* <p>If there are no directives specified in the recipe, then there would
* be no plugins to be loaded.</p>
*
* @return An empty set if there are not directives to be loaded dynamically,
* else the list of directives as specified in the recipe.
*/
public Set<String> getLoadableDirectives() {
return loadableDirectives;
}

/**
* Returns the version of the grammar as specified in the recipe. The
* version is the one extracted from Pragma. It's specified as follows
* <code>#pragma version 2.0;</code>
*
* @return version of the grammar used in the recipe.
*/
public String getVersion() {
return version;
}

/**
* Returns number of groups tokenized and parsed. The number returned will
* less than or equal to the number of directives specified in the recipe.
*
* <p>Fewer than number of directives is because of the '#pragma' directives</p>
* @return
*/
public int size() {
return tokens.size();
}

/**
* Returns an iterator to the list of token groups maintained by this object.
*
* @return iterator to the list of tokens maintained.
*/
public Iterator<TokenGroup> iterator() {
return tokens.iterator();
}

/**
* Static method for creating an instance of the {@code RecipeSymbol.Builder}.
*
* @return a instance of builder.
*/
public static RecipeSymbol.Builder builder() {
return new RecipeSymbol.Builder();
}

/**
* This method <code>toJson</code> returns the <code>JsonElement</code> object
* representation of this object.
*
* @return An instance of <code>JsonElement</code> representing this object.
*/
public JsonElement toJson() {
JsonObject output = new JsonObject();
output.addProperty("class", this.getClass().getSimpleName());
Expand All @@ -150,78 +87,40 @@ public JsonElement toJson() {
}

/**
* This inner class provides a builder pattern for building
* the <code>RecipeSymbol</code> object. In order to create the
* this builder, one has to use the static method defined in
* <code>RecipeSymbol</code>.
*
* Following is an example of how this can be done.
*
* <code>
* RecipeSymbol.Builder builder = RecipeSymbol.builder();
* builder.createTokenGroup(...);
* builder.addToken(...);
* builder.addVersion(...);
* builder.addLoadableDirective(...);
* RecipeSymbol compiled = builder.build();
* </code>
* Builder class for RecipeSymbol.
* Helps in constructing RecipeSymbol instances by accumulating tokens and
* metadata.
*/

public static final class Builder {
private final List<TokenGroup> groups = new ArrayList<>();
private final Set<String> loadableDirectives = new TreeSet<>();
private TokenGroup group = null;
private String version = "1.0";

/**
* <code>TokenGroup</code> is created for each directive in
* the recipe. This method creates a new <code>TokenGroup</code>
* by passing the <code>SourceInfo</code>, which represents the
* information of the source parsed.
*
* @param info about the source directive being parsed.
*/
public void createTokenGroup(SourceInfo info) {
if (group != null) {
groups.add(group);
}
this.group = new TokenGroup(info);
}

/**
* This method provides a way to add a <code>Token</code> to the <code>TokenGroup</code>.
*
* @param token to be added to the token group.
*/
public void addToken(Token token) {
group.add(token);
}

/**
* Recipe can specify the version of the grammar. This method
* allows one to extract and add the version to the <code>RecipeSymbol.</code>
*
* @param version of the recipe grammar being used.
*/
public void addToken(TimeDuration token) {
group.add((Token) token); // ✅ Cast to Token explicitly
}

public void addVersion(String version) {
this.version = version;
}

/**
* A Recipe can specify the pragma instructions for loading the directives
* dynamically. This method allows adding the new directive to be loaded
* as it's parsing through the call graph.
*
* @param directive to be loaded dynamically.
*/
public void addLoadableDirective(String directive) {
loadableDirectives.add(directive);
}

/**
* Returns a fully constructed and valid <code>RecipeSymbol</code> object.
*
* @return An instance of <code>RecipeSymbol</code>
*/
public RecipeSymbol build() {
groups.add(group);
return new RecipeSymbol(version, loadableDirectives, this.groups);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* 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.api.parser;

import java.util.Locale;

/**
* Parses a byte size string like "10KB", "1.5MB", "2GB", etc.
*/
public class ByteSize {
private final long bytes;

public ByteSize(String value) {
this.bytes = parseByteSize(value);
}

public long getBytes() {
return bytes;
}

private long parseByteSize(String value) {
String trimmed = value.trim().toUpperCase(Locale.ENGLISH);
double number;
String unit;

int index = 0;
while (index < trimmed.length() &&
(Character.isDigit(trimmed.charAt(index)) || trimmed.charAt(index) == '.' || trimmed.charAt(index) == '-')) {
index++;
}

if (index == 0) {
throw new IllegalArgumentException("No numeric value found in byte size: " + value);
}

number = Double.parseDouble(trimmed.substring(0, index));
unit = trimmed.substring(index).trim();

switch (unit) {
case "B":
case "":
return (long) number;
case "KB":
return (long) (number * 1024);
case "MB":
return (long) (number * 1024 * 1024);
case "GB":
return (long) (number * 1024 * 1024 * 1024);
case "TB":
return (long) (number * 1024L * 1024 * 1024 * 1024);
case "PB":
return (long) (number * 1024L * 1024 * 1024 * 1024 * 1024);
case "EB":
return (long) (number * 1024L * 1024 * 1024 * 1024 * 1024 * 1024);
default:
throw new IllegalArgumentException("Unknown byte size unit: " + unit);
}
}

@Override
public String toString() {
return bytes + " bytes";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* 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.api.parser;

import java.util.Locale;
import java.util.concurrent.TimeUnit;

/**
* Parses a time duration string like "150ms", "2s", "1.5m", "3h", etc.
*/
public class TimeDuration {
private final long durationMillis;

public TimeDuration(String value) {
this.durationMillis = parseDuration(value);
}

public long getDurationMillis() {
return durationMillis;
}

private long parseDuration(String value) {
String trimmed = value.trim().toLowerCase(Locale.ENGLISH);
double number;
String unit;

int index = 0;
while (index < trimmed.length() &&
(Character.isDigit(trimmed.charAt(index)) || trimmed.charAt(index) == '.' || trimmed.charAt(index) == '-')) {
index++;
}

if (index == 0) {
throw new IllegalArgumentException("No numeric value found in time duration: " + value);
}

number = Double.parseDouble(trimmed.substring(0, index));
unit = trimmed.substring(index).trim();

switch (unit) {
case "ms":
return (long) number;
case "s":
return (long) TimeUnit.SECONDS.toMillis((long) number);
case "m":
return (long) TimeUnit.MINUTES.toMillis((long) number);
case "h":
return (long) TimeUnit.HOURS.toMillis((long) number);
case "d":
return (long) TimeUnit.DAYS.toMillis((long) number);
default:
throw new IllegalArgumentException("Unknown time duration unit: " + unit);
}
}

@Override
public String toString() {
return durationMillis + " ms";
}
}
Loading