Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.javafx.fxml.expression;
import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.sun.javafx.fxml.BeanAdapter;
import static com.sun.javafx.fxml.expression.Operator.*;
/**
* Abstract base class for expressions. Also provides static methods for
* creating arithmetic and logical expressions as well as accessing namespace
* values by key path.
*/
public abstract class Expression {
// Expression parser class
private static class Parser {
public static class Token {
public Token(TokenType type, Object value) {
this.type = type;
this.value = value;
}
public final TokenType type;
public final Object value;
@Override
public String toString() {
return value.toString();
}
}
public enum TokenType {
LITERAL,
VARIABLE,
FUNCTION,
UNARY_OPERATOR,
BINARY_OPERATOR,
BEGIN_GROUP,
END_GROUP
}
private int c = -1;
private char[] pushbackBuffer = new char[PUSHBACK_BUFFER_SIZE];
private static final int PUSHBACK_BUFFER_SIZE = 6;
public Expression parse(Reader reader) throws IOException {
LinkedList tokens = tokenize(new PushbackReader(reader, PUSHBACK_BUFFER_SIZE));
LinkedList stack = new LinkedList<>();
for (Token token : tokens) {
Expression> expression;
switch (token.type) {
case LITERAL: {
expression = new LiteralExpression(token.value);
break;
}
case VARIABLE: {
expression = new VariableExpression((KeyPath)token.value);
break;
}
case FUNCTION: {
// TODO Create a new FunctionExpression type; this
// class will have a property of type Method that
// refers to a method defined by the "scope context"
// (e.g. an FXML document controller), which must be
// set prior to evaluating the expression; it will
// also have a list of argument Expressions
expression = null;
break;
}
case UNARY_OPERATOR: {
Operator operator = (Operator)token.value;
Expression operand = stack.pop();
switch(operator) {
case NEGATE:
expression = negate(operand);
break;
case NOT:
expression = not(operand);
break;
default:
throw new UnsupportedOperationException();
}
break;
}
case BINARY_OPERATOR: {
Operator operator = (Operator)token.value;
Expression right = stack.pop();
Expression left = stack.pop();
switch(operator) {
case ADD:
expression = add(left, right);
break;
case SUBTRACT:
expression = subtract(left, right);
break;
case MULTIPLY:
expression = multiply(left, right);
break;
case DIVIDE:
expression = divide(left, right);
break;
case MODULO:
expression = modulo(left, right);
break;
case GREATER_THAN:
expression = greaterThan(left, right);
break;
case GREATER_THAN_OR_EQUAL_TO:
expression = greaterThanOrEqualTo(left, right);
break;
case LESS_THAN:
expression = lessThan(left, right);
break;
case LESS_THAN_OR_EQUAL_TO:
expression = lessThanOrEqualTo(left, right);
break;
case EQUAL_TO:
expression = equalTo(left, right);
break;
case NOT_EQUAL_TO:
expression = notEqualTo(left, right);
break;
case AND:
expression = and(left, right);
break;
case OR:
expression = or(left, right);
break;
default:
throw new UnsupportedOperationException();
}
break;
}
default: {
throw new UnsupportedOperationException();
}
}
stack.push(expression);
}
if (stack.size() != 1) {
throw new IllegalArgumentException("Invalid expression.");
}
return stack.peek();
}
private LinkedList tokenize(PushbackReader reader) throws IOException {
// Read the string into a postfix list of tokens
LinkedList tokens = new LinkedList<>();
LinkedList stack = new LinkedList<>();
c = reader.read();
boolean unary = true;
while (c != -1) {
// Skip whitespace
while (c != -1 && Character.isWhitespace(c)) {
c = reader.read();
}
if (c != -1) {
Token token;
if (c == 'n') {
if (readKeyword(reader, NULL_KEYWORD)) {
token = new Token(TokenType.LITERAL, null);
} else {
token = new Token(TokenType.VARIABLE, KeyPath.parse(reader));
c = reader.read();
}
} else if (c == '"' || c == '\'') {
StringBuilder stringBuilder = new StringBuilder();
// Use the same delimiter to close the string
int t = c;
// Move to the next character after the delimiter
c = reader.read();
while (c != -1 && c != t) {
if (!Character.isISOControl(c)) {
if (c == '\\') {
c = reader.read();
if (c == 'b') {
c = '\b';
} else if (c == 'f') {
c = '\f';
} else if (c == 'n') {
c = '\n';
} else if (c == 'r') {
c = '\r';
} else if (c == 't') {
c = '\t';
} else if (c == 'u') {
StringBuilder unicodeValueBuilder = new StringBuilder();
while (unicodeValueBuilder.length() < 4) {
c = reader.read();
unicodeValueBuilder.append((char)c);
}
String unicodeValue = unicodeValueBuilder.toString();
c = (char)Integer.parseInt(unicodeValue, 16);
} else {
if (!(c == '\\'
|| c == '/'
|| c == '\"'
|| c == '\''
|| c == t)) {
throw new IllegalArgumentException("Unsupported escape sequence.");
}
}
}
stringBuilder.append((char)c);
}
c = reader.read();
}
if (c != t) {
throw new IllegalArgumentException("Unterminated string.");
}
// Move to the next character after the delimiter
c = reader.read();
token = new Token(TokenType.LITERAL, stringBuilder.toString());
} else if (Character.isDigit(c)) {
StringBuilder numberBuilder = new StringBuilder();
boolean integer = true;
while (c != -1 && (Character.isDigit(c) || c == '.'
|| c == 'e' || c == 'E')) {
numberBuilder.append((char)c);
integer &= !(c == '.');
c = reader.read();
}
Number value;
if (integer) {
value = Long.parseLong(numberBuilder.toString());
} else {
value = Double.parseDouble(numberBuilder.toString());
}
token = new Token(TokenType.LITERAL, value);
} else if (c == 't') {
if (readKeyword(reader, TRUE_KEYWORD)) {
token = new Token(TokenType.LITERAL, true);
} else {
token = new Token(TokenType.VARIABLE, KeyPath.parse(reader));
c = reader.read();
}
} else if (c == 'f') {
if (readKeyword(reader, FALSE_KEYWORD)) {
token = new Token(TokenType.LITERAL, false);
} else {
token = new Token(TokenType.VARIABLE, KeyPath.parse(reader));
c = reader.read();
}
} else if (Character.isJavaIdentifierStart(c)) {
reader.unread(c);
// TODO Here (and everywhere else where we call KeyPath.parse()),
// read the path value. If c == '(' when this method returns, the
// path refers to a function; read the arguments and create a
// FUNCTION token
token = new Token(TokenType.VARIABLE, KeyPath.parse(reader));
c = reader.read();
} else {
boolean readNext = true;
if (unary) {
switch(c) {
case '-':
token = new Token(TokenType.UNARY_OPERATOR, NEGATE);
break;
case '!':
token = new Token(TokenType.UNARY_OPERATOR, NOT);
break;
case '(':
token = new Token(TokenType.BEGIN_GROUP, null);
break;
default:
throw new IllegalArgumentException("Unexpected character in expression.");
}
} else {
switch(c) {
case '+':
token = new Token(TokenType.BINARY_OPERATOR, ADD);
break;
case '-':
token = new Token(TokenType.BINARY_OPERATOR, SUBTRACT);
break;
case '*':
token = new Token(TokenType.BINARY_OPERATOR, MULTIPLY);
break;
case '/':
token = new Token(TokenType.BINARY_OPERATOR, DIVIDE);
break;
case '%':
token = new Token(TokenType.BINARY_OPERATOR, MODULO);
break;
case '=':
c = reader.read();
if (c == '=') {
token = new Token(TokenType.BINARY_OPERATOR, EQUAL_TO);
} else {
throw new IllegalArgumentException("Unexpected character in expression.");
}
break;
case '!':
c = reader.read();
if (c == '=') {
token = new Token(TokenType.BINARY_OPERATOR, NOT_EQUAL_TO);
} else {
throw new IllegalArgumentException("Unexpected character in expression.");
}
break;
case '>':
c = reader.read();
if (c == '=') {
token = new Token(TokenType.BINARY_OPERATOR, GREATER_THAN_OR_EQUAL_TO);
} else {
readNext = false;
token = new Token(TokenType.BINARY_OPERATOR, GREATER_THAN);
}
break;
case '<':
c = reader.read();
if (c == '=') {
token = new Token(TokenType.BINARY_OPERATOR, LESS_THAN_OR_EQUAL_TO);
} else {
readNext = false;
token = new Token(TokenType.BINARY_OPERATOR, LESS_THAN);
}
break;
case '&':
c = reader.read();
if (c == '&') {
token = new Token(TokenType.BINARY_OPERATOR, AND);
} else {
throw new IllegalArgumentException("Unexpected character in expression.");
}
break;
case '|':
c = reader.read();
if (c == '|') {
token = new Token(TokenType.BINARY_OPERATOR, OR);
} else {
throw new IllegalArgumentException("Unexpected character in expression.");
}
break;
case ')':
token = new Token(TokenType.END_GROUP, null);
break;
default:
throw new IllegalArgumentException("Unexpected character in expression.");
}
}
if (readNext) {
c = reader.read();
}
}
// Process the token
switch (token.type) {
case LITERAL:
case VARIABLE: {
tokens.add(token);
break;
}
case UNARY_OPERATOR:
case BINARY_OPERATOR: {
int priority = ((Operator)token.value).getPriority();
while (!stack.isEmpty()
&& stack.peek().type != TokenType.BEGIN_GROUP
&& ((Operator)stack.peek().value).getPriority() >= priority
&& ((Operator)stack.peek().value).getPriority() != Operator.MAX_PRIORITY) {
tokens.add(stack.pop());
}
stack.push(token);
break;
}
case BEGIN_GROUP: {
stack.push(token);
break;
}
case END_GROUP: {
for (Token t = stack.pop(); t.type != TokenType.BEGIN_GROUP; t = stack.pop()) {
tokens.add(t);
}
break;
}
default: {
throw new UnsupportedOperationException();
}
}
unary = !(token.type == TokenType.LITERAL || token.type == TokenType.VARIABLE || token.type == TokenType.END_GROUP);
}
}
while (!stack.isEmpty()) {
tokens.add(stack.pop());
}
return tokens;
}
private boolean readKeyword(PushbackReader reader, String keyword) throws IOException {
int n = keyword.length();
int i = 0;
while (c != -1 && i < n) {
pushbackBuffer[i] = (char)c;
if (keyword.charAt(i) != c) {
break;
}
c = reader.read();
i++;
}
boolean result;
if (i < n) {
reader.unread(pushbackBuffer, 0, i + 1);
result = false;
} else {
result = true;
}
return result;
}
}
private static final String NULL_KEYWORD = "null";
private static final String TRUE_KEYWORD = "true";
private static final String FALSE_KEYWORD = "false";
/**
* Evaluates the expression.
*
* @param namespace
* The namespace against which the expression will be evaluated.
*
* @return
* The result of evaluating the expression.
*/
public abstract T evaluate(Object namespace);
/**
* Updates the expression value.
*
* @param namespace
* The namespace against which the expression will be evaluated.
*
* @param value
* The value to assign to the expression.
*/
public abstract void update(Object namespace, T value);
/**
* Tests whether the expression is defined.
*
* @param namespace
* The namespace against which the expression will be evaluated.
*
* @return
* true if the expression is defined; false, otherwise.
*/
public abstract boolean isDefined(Object namespace);
/**
* Tests whether the expression represents an l-value (i.e. can be
* assigned to).
*
* @return
* true if the expression is an l-value; false,
* otherwise.
*/
public abstract boolean isLValue();
/**
* Returns a list of arguments to this expression.
*/
public List getArguments() {
ArrayList arguments = new ArrayList<>();
getArguments(arguments);
return arguments;
}
/**
* Populates a list of arguments to this expression.
*/
protected abstract void getArguments(List arguments);
/**
* Returns the value at a given path within a namespace.
*
* @param namespace
* @param keyPath
*
* @return
* The value at the given path, or null if no such value exists.
*/
@SuppressWarnings("unchecked")
public static T get(Object namespace, KeyPath keyPath) {
if (keyPath == null) {
throw new NullPointerException();
}
return (T)get(namespace, keyPath.iterator());
}
/**
* Returns the value at a given path within a namespace.
*
* @param namespace
* @param keyPathIterator
*
* @return
* The value at the given path, or null if no such value exists.
*/
@SuppressWarnings("unchecked")
private static T get(Object namespace, Iterator keyPathIterator) {
if (keyPathIterator == null) {
throw new NullPointerException();
}
T value;
if (keyPathIterator.hasNext()) {
// TODO Remove cast to T when build is updated to Java 7
value = (T)get(get(namespace, keyPathIterator.next()), keyPathIterator);
} else {
value = (T)namespace;
}
return value;
}
/**
* Returns the value at a given key within a namespace.
*
* @param namespace
* @param key
*
* @return
* The value at the given key, or null if no such value exists.
*/
@SuppressWarnings("unchecked")
public static T get(Object namespace, String key) {
if (key == null) {
throw new NullPointerException();
}
Object value;
if (namespace instanceof List>) {
List