parser.java
来自「java实现浏览器等本地桌面的功能」· Java 代码 · 共 336 行
JAVA
336 行
/* * $Id: Parser.java,v 1.5 2005/10/10 17:01:01 rbair Exp $ * * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */package org.jdesktop.dataset;import java.text.MessageFormat;import java.util.Iterator;import java.util.List;import net.sf.jga.fn.BinaryFunctor;import net.sf.jga.fn.Generator;import net.sf.jga.fn.UnaryFunctor;import net.sf.jga.fn.adaptor.ApplyUnary;import net.sf.jga.fn.adaptor.Constant;import net.sf.jga.fn.adaptor.ConstantUnary;import net.sf.jga.fn.adaptor.Identity;import net.sf.jga.fn.algorithm.Accumulate;import net.sf.jga.fn.algorithm.Count;import net.sf.jga.fn.algorithm.TransformUnary;import net.sf.jga.fn.arithmetic.Average;import net.sf.jga.fn.arithmetic.Divides;import net.sf.jga.fn.arithmetic.Plus;import net.sf.jga.fn.arithmetic.ValueOf;import net.sf.jga.fn.comparison.Max;import net.sf.jga.fn.comparison.Min;import net.sf.jga.fn.property.ArrayBinary;import net.sf.jga.fn.property.Construct;import net.sf.jga.fn.property.GetProperty;import net.sf.jga.fn.property.InvokeMethod;import net.sf.jga.fn.property.InvokeNoArgMethod;import net.sf.jga.parser.JFXGParser;import net.sf.jga.parser.FunctorRef;import net.sf.jga.parser.GeneratorRef;import net.sf.jga.parser.ParseException;import net.sf.jga.parser.UnaryFunctorRef;import net.sf.jga.util.FilterIterator;// NOTE: throughout this class, a generic parm <DataRow> had to be omitted, and has been// replaced with '/**/'. The getRowCountFn couldn't use the fully specified generic type// List<DataRow>, because the compiler could not detect that List.class was the proper// value for an argument of type Class<List<DataRow>>. This may have been fixed in a// later 1.5_0x compiler.class Parser extends JFXGParser { private DataTable table; boolean inTableContext = false; // Functor that returns the list of rows for a given table private UnaryFunctor<DataTable,List/**/> getRowsFn = new GetProperty<DataTable,List/**/>(DataTable.class, "Rows"); // Functor that returns the number of rows in a list private UnaryFunctor<DataTable,Integer> getRowCountFn = new InvokeNoArgMethod<List/**/,Integer>(List.class, "size").compose(getRowsFn); // Functor that returns an iterator over the rows in a list private UnaryFunctor<DataTable,Iterator/**/> iterateTableFn = new InvokeNoArgMethod<List/**/,Iterator/**/>(List.class, "iterator") .compose(getRowsFn); // Functor that returns the value of a DataValue private UnaryFunctor<DataValue,?> getValueFn = new GetProperty<DataValue,Object>(DataValue.class, "Value"); // Functor that constructs a FilterIterator, given an InputIterator and a Predicate private Class[] filterCtorArgs = new Class[]{Iterator.class,UnaryFunctor.class}; private BinaryFunctor<Iterator/**/,UnaryFunctor<DataRow,Boolean>,? extends Iterator/**/> makeFilterFn = new Construct<FilterIterator/**/>(filterCtorArgs,FilterIterator.class) .compose(new ArrayBinary<Iterator/**/,UnaryFunctor<DataRow,Boolean>>()); // ============================= // DataSet specific entry points // ============================= /** * Parses a computed column expression, for the given table. */ UnaryFunctor<DataRow,?> parseComputedColumn(DataTable table, String expression) throws ParseException { setCurrentTable(table); try { return parseUnary(expression, DataRow.class); } finally { setCurrentTable(null); } } /** * Parses an expression that computes a summary value for the collection of data * currently available in the bound dataset. */ Generator<?> parseDataValue(String expression) throws ParseException { inTableContext = true; try { return parseGenerator(expression); } finally { inTableContext = false; setCurrentTable(null); } } // ============================= // FunctorParser extensions // ============================= protected FunctorRef reservedWord(String name) throws ParseException { // If we're expecting a table but we don't know which one, then see if the name // is the name of a table. If so, then this is the table we're expecting. if (table == null) { DataTable maybeTable = ((DataSet) getBoundObject()).getTable(name); if (maybeTable != null) { setCurrentTable(maybeTable); return new GeneratorRef(new Constant<DataTable>(table), DataTable.class); } } // Next, see if the name is the same as the current table then return a // constant reference to the table if (table != null && name.equals(table.getName())) return new GeneratorRef(new Constant<DataTable>(table), DataTable.class); // Otherwise, if we already have a current table, then see if the name is // a column reference within the table if (table != null) { DataColumn col = table.getColumn(name); if (col != null) return makeColumnRef(table, col); } return null; } /** * Allows for (not necessarily constant) predefined fields to be added to the grammar */ protected FunctorRef reservedField(FunctorRef prefix, String name) throws ParseException { if (isTableReference(prefix) && getReferencedTable(prefix).equals(table)) { DataColumn col = table.getColumn(name); if (col != null) { return makeColumnRef(table, col); } } return super.reservedField(prefix, name); } /** * Allows for function-style names to be added to the grammar. */ protected FunctorRef reservedFunction(String name, FunctorRef[] args) throws ParseException{ if (inTableContext) { assert table != null; // if the last argument returns a boolean, then it is interpreted as a // filter on the table: only those rows that meet the filter condition // are included in the computation FunctorRef lastArgRef = args[args.length - 1]; boolean hasFilter = (lastArgRef.getReturnType() == Boolean.class); UnaryFunctor<DataRow,Boolean> filter = (hasFilter) ? ((UnaryFunctorRef) lastArgRef).getFunctor(): new ConstantUnary<DataRow,Boolean>(Boolean.TRUE); // If count doesn't have a filter, then it doesn't require iterating over the // rows of the table: having the list of rows is good enough if ("count".equals(name)) { if (hasFilter) { // Iterates the table, counting those that satisfy the filter. Count // returns Long, so we have to convert to Integer. UnaryFunctor<DataTable,Integer> countFn = new ValueOf<Long,Integer>(Integer.class) .compose(new Count(filter)) .compose(iterateTableFn); return new GeneratorRef(countFn.bind(table),Long.class); } else { return new GeneratorRef(getRowCountFn.bind(table), Integer.class); } } UnaryFunctor<DataTable,? extends Iterator/**/> filterTableFn = (hasFilter) ? makeFilterFn.bind2nd(filter).compose(iterateTableFn): iterateTableFn; Generator<? extends Iterator/**/> iterateRows = filterTableFn.bind(table); // The remaining functions evaluate an expression on every qualifying row // in the table. The first argument is the expression to be evaluated, // so we'll set up a transform that returns the value of the expression // when passed the row Class type = args[0].getReturnType(); if (type.isPrimitive()) type = getBoxedType(type); TransformUnary xform = new TransformUnary(((UnaryFunctorRef) args[0]).getFunctor()); // Average doesn't have an easy way to compute without iterating, but the // functor (like Count) takes an iteration and returns the computed value if ("avg".equals(name)) { validateArgument(Number.class, type, name); Average avg = new Average(type); return new GeneratorRef(avg.generate(xform.generate(iterateRows)), type); } // All of the other summary functions require iterating over the list of // rows in the table, and applying a binary function to the current value // and the previous intermediate value BinaryFunctor bf = null; if ("max".equals(name)) { validateArgument(Comparable.class, type, name); bf = new Max(new net.sf.jga.util.ComparableComparator()); } else if ("min".equals(name)) { validateArgument(Comparable.class, type, name); bf = new Min(new net.sf.jga.util.ComparableComparator()); } else if ("sum".equals(name)) { validateArgument(Number.class, type, name); bf = new Plus(type); } // The simpler summary functions only require an iteration, with no further // processing. if (bf != null) { Generator gen = new Accumulate(bf).generate(xform.generate(iterateRows)); return new GeneratorRef(gen, type); } } return super.reservedFunction(name, args); } // ====================== // implementation details // ====================== /** * Sets the table against which column references will be created. There can only be * a single table involved in any given expression. */ private void setCurrentTable(DataTable table) throws ParseException { if (this.table != null && table != null) throw new ParseException("Parser is currently associated with table " +this.table); this.table = table; } /** * Builds a functor for a given table and column. The functor takes a row in the * table and returns the value of the appropriate column. */ private UnaryFunctorRef makeColumnRef(DataTable table, DataColumn column) { // Builds a functor that takes a Row, and returns an array consisting // of that row and the column we've been given ApplyUnary<DataRow> args = new ApplyUnary<DataRow>(new UnaryFunctor[] { new Identity<DataRow>(),new ConstantUnary<DataRow,DataColumn>(column) }); // getValue(col,row) method as a functor InvokeMethod<DataTable,?> getValue = new InvokeMethod<DataTable,Object>(DataTable.class,"getValue", new Class[] {DataRow.class, DataColumn.class}); // tie the two together. The result is a functor that takes a row and returns // the value in the designated column UnaryFunctor<DataRow,?> value = getValue.bind1st(table).compose(args); // Return a parser description of the functor we've built. return new UnaryFunctorRef(value, DataRow.class, ARG_NAME[0], column.getType()); } /** * returns true if the functor reference is one that returns a specific data table. */ private boolean isTableReference(FunctorRef ref) { return ref != null && ref.getReturnType().equals(DataTable.class) && ref.getReferenceType() == FunctorRef.CONSTANT; } /** * returns the specific table to which the given functor refers. Assumes isTableReference(ref), * will throw ClassCastException if not */ private DataTable getReferencedTable(FunctorRef ref) { return (DataTable)((GeneratorRef) ref).getFunctor().gen(); } /** * Constructs and throws a ParseException if the return type of a subexpression * (the found type) is not a subtype of the type required for the given function. */ private void validateArgument(Class reqType, Class foundType, String fnName) throws ParseException { if (reqType.isAssignableFrom(foundType)) { return; } String msg = "Unable to compute {0} of type {1}"; Object[] msgargs = new Object[] { fnName, foundType.getSimpleName() }; throw new ParseException(MessageFormat.format(msg, msgargs)); }}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?