cdc.applic.expressions.Expression Maven / Gradle / Ivy
Show all versions of cdc-applic-expressions Show documentation
package cdc.applic.expressions;
import java.util.ArrayList;
import java.util.List;
import cdc.applic.expressions.ast.FalseNode;
import cdc.applic.expressions.ast.ParsingNode;
import cdc.applic.expressions.ast.TrueNode;
import cdc.applic.expressions.parsing.Parser;
import cdc.applic.expressions.parsing.Recognizer;
public class Expression implements Comparable {
/**
* String representation of the Expression, as defined at creation time.
*/
private String content;
/**
* The AST root node.
*/
private ParsingNode root = null;
/**
* The formatting to use to obtain the most compact string content.
*/
public static final Formatting COMPRESS_FORMATTING = Formatting.SHORT_NARROW;
public static final Expression TRUE = new Expression("true");
public static final Expression FALSE = new Expression("false");
/**
* Default expression pool.
*/
public static final ExpressionPool DEFAULT_POOL = new ExpressionPool();
/**
* If set to {@code true}, {@link #fromString(String)} uses {@link #DEFAULT_POOL}.
*/
public static boolean useDefaultPool = true;
/**
* Creates an expression and checks its syntax if required.
*
* @param content The expression content.
* @param parse If {@code true}, {@code content } is parsed.
* @throws LexicalException When this expression is lexically invalid and {@code parse} is {@code true}.
* @throws SyntacticException When this expression is syntactically invalid and {@code parse} is {@code true}.
*/
public Expression(String content,
boolean parse) {
if (content == null || content.trim().isEmpty()) {
this.content = "true";
} else {
this.content = content;
}
if (parse) {
getRootNode();
}
}
/**
* Creates an expression and checks its syntax.
*
* @param content The expression content.
* @throws LexicalException When this expression is lexically invalid.
* @throws SyntacticException When this expression is syntactically invalid.
*/
public Expression(String content) {
this(content, true);
}
/**
* Creates an expression from a node.
*
* @param root The root node.
* @param formatting The optional formating used to create string representation of the expression.
* If {@code null}, the string representation will be created lazily using default formatting.
*/
public Expression(ParsingNode root,
Formatting formatting) {
if (formatting != null) {
this.content = root.toInfix(formatting);
}
this.root = root;
}
/**
* Creates an expression from a node, and creates a string representation using default formatting.
*
* @param root The root node.
*/
public Expression(ParsingNode root) {
this(root, COMPRESS_FORMATTING);
}
/**
* @return The text representation of this expression, as defined at creation.
*/
public String getContent() {
if (content == null) {
content = root.toInfix(COMPRESS_FORMATTING);
}
return content;
}
/**
* @return The AST root node.
* @throws LexicalException When this expression is lexically invalid.
* @throws SyntacticException When this expression is syntactically invalid.
*/
public ParsingNode getRootNode() {
if (root == null) {
final Parser parser = new Parser();
root = parser.parse(content);
}
return root;
}
/**
* @return {@code true} if this expression is syntactically valid.
*/
public boolean isValid() {
// If root is null, content is not null
return root != null || isValid(content);
}
/**
* Checks the validity of the expression.
*
* @throws LexicalException When this expression is lexically invalid.
* @throws SyntacticException When this expression is syntactically invalid.
*/
public void checkValidity() {
getRootNode();
}
/**
* @return {@code true} if this expression is simply true.
*/
public boolean isTrue() {
return getRootNode() instanceof TrueNode;
}
/**
* @return {@code true} if this expression is simply false.
*/
public boolean isFalse() {
return getRootNode() instanceof FalseNode;
}
/**
* @param expression The expression text.
* @return {@code true} if {@code expression} can be parsed as a valid Expression.
*/
public static boolean isValid(String expression) {
final Recognizer recognizer = new Recognizer();
return recognizer.isValid(expression);
}
/**
* Converts a string to an Expression.
*
* {@link #DEFAULT_POOL} is used if {@link #useDefaultPool} is set to {@code true}.
*
* @param content The expression content.
* @return The Expression corresponding to {@code content} or {@code null} if content is {@code null}.
*/
public static Expression fromString(String content) {
return content == null
? null
: (useDefaultPool ? DEFAULT_POOL.get(content) : new Expression(content));
}
public static Expression of(String content) {
return new Expression(content);
}
public static Expression of(String content,
boolean parse) {
return new Expression(content, parse);
}
/**
* @param expression The expression.
* @return The string representation of {@code expression}.
*/
public static String asString(Expression expression) {
return expression.getContent();
}
public static ParsingNode toNode(Expression expression) {
return expression.getRootNode();
}
public static Expression fromNode(ParsingNode node) {
return new Expression(node);
}
public static ParsingNode[] toNodeArray(Expression... expressions) {
final ParsingNode[] result = new ParsingNode[expressions.length];
for (int index = 0; index < result.length; index++) {
result[index] = expressions[index].getRootNode();
}
return result;
}
public static ParsingNode[] toNodeArray(List expressions) {
final ParsingNode[] result = new ParsingNode[expressions.size()];
for (int index = 0; index < result.length; index++) {
result[index] = expressions.get(index).getRootNode();
}
return result;
}
public static List toNodeList(Expression... expressions) {
final List result = new ArrayList<>(expressions.length);
for (final Expression expression : expressions) {
result.add(expression.getRootNode());
}
return result;
}
public static List toNodeList(List expressions) {
final List result = new ArrayList<>(expressions.size());
for (final Expression expression : expressions) {
result.add(expression.getRootNode());
}
return result;
}
public static List toExpressionList(String... expressions) {
final List result = new ArrayList<>(expressions.length);
for (final String expression : expressions) {
result.add(new Expression(expression));
}
return result;
}
/**
* @param formatting The formatting.
* @return A infix representation of this expression using {@code formatting}.
*/
public String toInfix(Formatting formatting) {
if (content == null && (formatting == null || formatting.equals(COMPRESS_FORMATTING))) {
// This expression was built from a node and null formatting.
// We can computes its content.
return getContent();
} else {
return getRootNode().toInfix(formatting);
}
}
/**
* @return An expression equivalent to this one and whose text is compressed.
* @throws LexicalException When this expression is lexically invalid.
* @throws SyntacticException When this expression is syntactically invalid.
*/
public Expression compress() {
if (content == null) {
// The expression was built from a node and a null formatting
// We now its text will use compress formatting
return this;
} else {
// We build a new expression from root node and null formatting
return new Expression(getRootNode(), null);
}
}
@Override
public int compareTo(Expression o) {
return getContent().compareTo(o.getContent());
}
@Override
public int hashCode() {
return getContent().hashCode();
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof Expression)) {
return false;
}
final Expression other = (Expression) object;
return getContent().equals(other.getContent());
}
@Override
public String toString() {
return getContent();
}
}