Package org.geotools.filter.text.ecql

Source Code of org.geotools.filter.text.ecql.ECQLCompiler

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2006-2008, Open Source Geospatial Foundation (OSGeo)
*
*    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;
*    version 2.1 of the License.
*
*    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.
*/
package org.geotools.filter.text.ecql;

import java.io.StringReader;
import java.util.List;

import org.geotools.filter.IllegalFilterException;
import org.geotools.filter.text.commons.IToken;
import org.geotools.filter.text.commons.Result;
import org.geotools.filter.text.commons.TokenAdapter;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.generated.parsers.ECQLParser;
import org.geotools.filter.text.generated.parsers.Node;
import org.geotools.filter.text.generated.parsers.ParseException;
import org.geotools.filter.text.generated.parsers.TokenMgrError;
import org.opengis.filter.BinaryComparisonOperator;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.Not;
import org.opengis.filter.Or;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.spatial.BinarySpatialOperator;
import org.opengis.filter.spatial.DistanceBufferOperator;
import org.opengis.filter.temporal.After;
import org.opengis.filter.temporal.Before;
import org.opengis.filter.temporal.During;


/**
* ECQLCompiler
*
* <p>
* Builds the filter, expression or arguments related with the visited
* node of syntax tree
* </p>
*
* @author Jody Garnett
* @author Mauricio Pazos (Axios Engineering)
*
*
*
*
* @source $URL$
* @version Revision: 1.9
* @since 2.6
*/
public class ECQLCompiler extends ECQLParser implements org.geotools.filter.text.commons.ICompiler{
   
    private static final String ATTRIBUTE_PATH_SEPARATOR = "/";

    /** cql expression to compile */
    private final String           source;

    private final ECQLFilterBuilder builder;

    /**
     * new instance of TXTCompiler
     * @param txtSource
     * @param filterFactory
     */
    public ECQLCompiler(final String txtSource, final FilterFactory filterFactory) {
       
        super(new StringReader(txtSource));

        assert txtSource != null: "txtSource cannot be null";
        assert filterFactory != null: "filterFactory cannot be null";
       
        this.source = txtSource;
        this.builder =  new ECQLFilterBuilder(txtSource, filterFactory);
    }

    /**
     * compile source to produce a Filter. The filter
     * result must be retrieved with {@link #getFilter()}.
     *
     * @throws CQLException
     */
    public void compileFilter() throws CQLException{
        try {
            super.FilterCompilationUnit();
        } catch (TokenMgrError tokenError){
            throw new CQLException(tokenError.getMessage(),getTokenInPosition(0), this.source);
        } catch(CQLException e){
            throw e;
           
        } catch (ParseException e) {
            throw new CQLException(e.getMessage(), getTokenInPosition(0), e.getCause(), this.source);
        }
    }
   
    /**
     * compiles source to produce a Expression
     */
    public void compileExpression() throws CQLException{
        try {
            super.ExpressionCompilationUnit();
        } catch (TokenMgrError tokenError){
            throw new CQLException(tokenError.getMessage(),getTokenInPosition(0), this.source);
        } catch(CQLException e){
            throw e;
        } catch (ParseException e) {
            throw new CQLException(e.getMessage(), getTokenInPosition(0), e.getCause(), this.source);
        }
    }

    /**
     * Compiles a list of filters
     */
    public void compileFilterList() throws CQLException{
        try {
            super.FilterListCompilationUnit();
        } catch (TokenMgrError tokenError){
            throw new CQLException(tokenError.getMessage(),getTokenInPosition(0), this.source);
        } catch(CQLException e){
            throw e;
        } catch (ParseException e) {
            throw new CQLException(e.getMessage(), getTokenInPosition(0), e.getCause(), this.source);
        }
    }

    /**
     * @return the ECQLsource
     */
    public final String getSource(){
        return this.source;
    }

    /**
     * Return the filter resultant of compiling process
     * @return Filter
     * @throws CQLException
     */
    public final Filter getFilter() throws CQLException {
        return this.builder.getFilter();
    }
    /**
     * Return the expression resultant of compiling process
     * @return Expression
     * @throws CQLException
     */
    public final Expression getExpression() throws CQLException {

        return this.builder.getExpression();
    }
   
    public IToken getTokenInPosition(int index ){
        return TokenAdapter.newAdapterFor( super.getToken(index));
    }
   

    /**
     * Returns the list of Filters built as the result of calling
     * {@link #FilterListCompilationUnit()()}
     *
     * @return List<Filter>
     * @throws CQLException
     *             if a ClassCastException occurs while casting a built item to
     *             a Filter.
     */
    public List<Filter> getFilterList() throws CQLException {
        return this.builder.getFilterList();
    }
   
   

    public final void jjtreeOpenNodeScope(Node n) {
    }

    /**
     * called by parser when the node is closed.
     */
    public final void jjtreeCloseNodeScope(Node n) throws ParseException {
       
        try {
            Object built = build(n);

            IToken tokenAdapter = TokenAdapter.newAdapterFor(this.token);
            Result r = new Result(built, tokenAdapter, n.getType());
            this.builder.pushResult(r );
       
        } catch (CQLException e) {
                throw e;
             
        } finally {
            n.dispose();
        }
    }
    /**
     * This method is called when the parser close a node. Here is built the
     * filters an expressions recognized in the parsing process.
     *
     * @param n a Node instance
     * @return Filter or Expression
     * @throws CQLException
     */
    private Object build(Node n) throws CQLException {

        switch (n.getType()) {

            // ----------------------------------------
            // (+|-) Integer and Float
            // ----------------------------------------
            case JJTINTEGERNODE:
                return this.builder.buildLiteralInteger(getTokenInPosition(0).toString());
            case JJTFLOATINGNODE:
                return this.builder.buildLiteralDouble(getTokenInPosition(0).toString());
            case JJTNEGATIVENUMBER_NODE:
                return this.builder.bulidNegativeNumber();
           
                // ----------------------------------------
                // String
                // ----------------------------------------
            case JJTSTRINGNODE:
                return this.builder.buildLiteralString(getTokenInPosition(0)
                        .toString());

                // ----------------------------------------
                // Identifier
                // ----------------------------------------
            case JJTIDENTIFIER_NODE:
                return this.builder.buildIdentifier(JJTIDENTIFIER_PART_NODE);

            case JJTIDENTIFIER_PART_NODE:
                return this.builder.buildIdentifierPart(getTokenInPosition(0));

                // ----------------------------------------
                // attribute
                // ----------------------------------------
            case JJTSIMPLE_ATTRIBUTE_NODE:
                return this.builder.buildSimpleAttribute();

            case JJTCOMPOUND_ATTRIBUTE_NODE:
                return this.builder.buildCompoundAttribute(
                        JJTSIMPLE_ATTRIBUTE_NODE, ATTRIBUTE_PATH_SEPARATOR);

                // ----------------------------------------
                // function
                // ----------------------------------------
            case JJTFUNCTION_NODE:
                return this.builder.buildFunction(JJTFUNCTIONNAME_NODE);

            case JJTFUNCTIONNAME_NODE:
                return n; // used as mark of function name in stack

            case JJTFUNCTIONARG_NODE:
                return n; // used as mark of args in stack

                // Math Nodes
            case JJTADDNODE:
            case JJTSUBTRACTNODE:
            case JJTMULNODE:
            case JJTDIVNODE:
                return buildBinaryExpression(n.getType());

                // Boolean expression
            case JJTBOOLEAN_AND_NODE:
                return buildLogicFilter(JJTBOOLEAN_AND_NODE);

            case JJTBOOLEAN_OR_NODE:
                return buildLogicFilter(JJTBOOLEAN_OR_NODE);

            case JJTBOOLEAN_NOT_NODE:
                return buildLogicFilter(JJTBOOLEAN_NOT_NODE);

                // ----------------------------------------
                // between predicate actions
                // ----------------------------------------
            case JJTBETWEEN_NODE:
                return this.builder.buildBetween();

            case JJTNOT_BETWEEN_NODE:
                return this.builder.buildNotBetween();

                // ----------------------------------------
                // Compare predicate actions
                // ----------------------------------------
            case JJTCOMPARISONPREDICATE_EQ_NODE:
            case JJTCOMPARISONPREDICATE_GT_NODE:
            case JJTCOMPARISONPREDICATE_LT_NODE:
            case JJTCOMPARISONPREDICATE_GTE_NODE:
            case JJTCOMPARISONPREDICATE_LTE_NODE:
                return buildBinaryComparasionOperator(n.getType());

            case JJTCOMPARISONPREDICATE_NOT_EQUAL_NODE:

                Filter eq = buildBinaryComparasionOperator(JJTCOMPARISONPREDICATE_EQ_NODE);
                Not notFilter = this.builder.buildNotFilter(eq);

                return notFilter;

                // ----------------------------------------
                // Text predicate (Like)
                // ----------------------------------------
            case JJTLIKE_NODE:
                return this.builder.buildLikeFilter(true);

            case JJTNOT_LIKE_NODE:

                return this.builder.buildNotLikeFilter(true);

                // ----------------------------------------
                // Text predicate (ILike)
                // ----------------------------------------
            case JJTILIKE_NODE:
                return this.builder.buildLikeFilter(false);

            case JJTNOT_ILIKE_NODE:

                return this.builder.buildNotLikeFilter(false);

                // ----------------------------------------
                // Null predicate
                // ----------------------------------------
            case JJTNULLPREDICATENODE:
                return this.builder.buildPropertyIsNull();

            case JJTNOTNULLPREDICATENODE:
                return this.builder.buildPorpertyNotIsNull();

                // ----------------------------------------
                // temporal predicate actions
                // ----------------------------------------
            case JJTDATE_NODE:
                return this.builder
                        .buildDateExpression(getTokenInPosition(0));

            case JJTDATETIME_NODE:
                return this.builder
                        .buildDateTimeExpression(getTokenInPosition(0));

            case JJTDURATION_DATE_NODE:
                return this.builder
                        .buildDurationExpression(getTokenInPosition(0));

            case JJTPERIOD_BETWEEN_DATES_NODE:
                return this.builder.buildPeriodBetweenDates();

            case JJTPERIOD_WITH_DATE_DURATION_NODE:
                return this.builder.buildPeriodDateAndDuration();

            case JJTPERIOD_WITH_DURATION_DATE_NODE:
                return this.builder.buildPeriodDurationAndDate();

            case JJTTPTEQUALS_DATETIME_NODE:
                return this.builder.buildTEquals();

            case JJTTPBEFORE_DATETIME_NODE:
                return buildBefore();

            case JJTTPAFTER_DATETIME_NODE:
                return buildAfterPredicate();

            case JJTTPDURING_PERIOD_NODE:
                return buildDuring();

            case JJTTPBEFORE_OR_DURING_PERIOD_NODE:
                return buildBeforeOrDuring();

            case JJTTPDURING_OR_AFTER_PERIOD_NODE:
                return buildDuringOrAfter();

                // ----------------------------------------
                // existence predicate actions
                // ----------------------------------------
            case JJTEXISTENCE_PREDICATE_EXISTS_NODE:
                return this.builder.buildPropertyExists();

            case JJTEXISTENCE_PREDICATE_DOESNOTEXIST_NODE:

                Filter filter = this.builder.buildPropertyExists();
                Filter filterPropNotExist = this.builder.buildNotFilter(filter);

                return filterPropNotExist;

                // ----------------------------------------
                // routine invocation Geo Operation
                // ----------------------------------------
            case JJTROUTINEINVOCATION_GEOOP_EQUAL_NODE:
            case JJTROUTINEINVOCATION_GEOOP_DISJOINT_NODE:
            case JJTROUTINEINVOCATION_GEOOP_INTERSECT_NODE:
            case JJTROUTINEINVOCATION_GEOOP_TOUCH_NODE:
            case JJTROUTINEINVOCATION_GEOOP_CROSS_NODE:
            case JJTROUTINEINVOCATION_GEOOP_WITHIN_NODE:
            case JJTROUTINEINVOCATION_GEOOP_CONTAIN_NODE:
            case JJTROUTINEINVOCATION_GEOOP_OVERLAP_NODE:
                return buildBinarySpatialOperator(n.getType());

            case JJTROUTINEINVOCATION_GEOOP_BBOX_NODE:
            case JJTROUTINEINVOCATION_GEOOP_BBOX_SRS_NODE:
                return buildBBox(n.getType());

            case JJTROUTINEINVOCATION_GEOOP_RELATE_NODE:
              return this.builder.buildSpatialRelateFilter();
             
            case JJTDE9IM_NODE:
              return this.builder.buildDE9IM( getToken(0).image)

                // ----------------------------------------
                // routine invocation RelGeo Operation
                // ----------------------------------------
            case JJTTOLERANCE_NODE:
                return this.builder.buildTolerance();

            case JJTDISTANCEUNITS_NODE:
                return this.builder.buildDistanceUnit(getTokenInPosition(0));

            case JJTROUTINEINVOCATION_RELOP_BEYOND_NODE:
            case JJTROUTINEINVOCATION_RELOP_DWITHIN_NODE:
                return buildDistanceBufferOperator(n.getType());

                // ----------------------------------------
                // Geometries:
                // ----------------------------------------
            case JJTPOINT_NODE:
                return this.builder.buildCoordinate();
               
            case JJTPOINTTEXT_NODE:
                return this.builder.buildPointText();
               
            case JJTLINESTRINGTEXT_NODE:
                return this.builder.buildLineString(JJTPOINT_NODE);
               
            case JJTPOLYGONTEXT_NODE:
                return this.builder.buildPolygon(JJTLINESTRINGTEXT_NODE);
               
            case JJTMULTIPOINTTEXT_NODE:
                return this.builder.buildMultiPoint(JJTPOINTTEXT_NODE);
               
            case JJTMULTILINESTRINGTEXT_NODE:
                return this.builder.buildMultiLineString(JJTLINESTRINGTEXT_NODE);
               
            case JJTMULTIPOLYGONTEXT_NODE:
                return this.builder.buildMultiPolygon(JJTPOLYGONTEXT_NODE);
               
            case JJTGEOMETRYLITERAL:
                return this.builder.buildGeometryLiteral();

            case JJTGEOMETRYCOLLECTIONTEXT_NODE:
                return this.builder.buildGeometryCollection(JJTGEOMETRYLITERAL);
               
               
            case JJTWKTNODE:
                return this.builder.buildGeometry();
               
            case JJTENVELOPETAGGEDTEXT_NODE:
                return this.builder.buildEnvelop(TokenAdapter.newAdapterFor(n
                        .getToken()));

            case JJTINCLUDE_NODE:
                return Filter.INCLUDE;

            case JJTEXCLUDE_NODE:
                return Filter.EXCLUDE;

            case JJTTRUENODE:
                return this.builder.buildTrueLiteral();

            case JJTFALSENODE:
                return this.builder.buildFalseLiteral();

                // ----------------------------------------
                //  ID Predicate
                // ----------------------------------------
            case JJTFEATURE_ID_NODE:
                return this.builder.buildFeatureID(getTokenInPosition(0));

            case JJTID_PREDICATE_NODE:
                return this.builder.buildFilterId(JJTFEATURE_ID_NODE);
                // ----------------------------------------
                //  IN Predicate
                // ----------------------------------------
            case JJTIN_PREDICATE_NODE:
                return this.builder.buildInPredicate(JJTEXPRESSION_IN_LIST_NODE);
               
            case JJTNOT_IN_PREDICATE_NODE:
                Or orFilter = this.builder.buildInPredicate(JJTEXPRESSION_IN_LIST_NODE);
                Not notOrFilter = this.builder.buildNotFilter(orFilter);
                   
                return notOrFilter;
        }

        return null;
    }

    private org.opengis.filter.expression.BinaryExpression buildBinaryExpression(
            int nodeType) throws CQLException {
       
        org.opengis.filter.expression.BinaryExpression expr = null;

        switch (nodeType) {
        case JJTADDNODE:
            expr =  this.builder.buildAddExpression();

            break;

        case JJTSUBTRACTNODE:
            expr = this.builder.buildSubtractExression();

            break;

        case JJTMULNODE:
           
            expr = this.builder.buildMultiplyExpression();
            break;

        case JJTDIVNODE:
           
            expr = this.builder.buildDivideExpression();           
            break;

        default:
            break;
        }

        return expr;
    }

    private Filter buildLogicFilter(int nodeType)
            throws CQLException {
        try {
            Filter logicFilter;

            switch (nodeType) {
            case JJTBOOLEAN_AND_NODE:
                logicFilter = this.builder.buildAndFilter();
                break;

            case JJTBOOLEAN_OR_NODE:
                logicFilter = this.builder.buildOrFilter();
                break;

            case JJTBOOLEAN_NOT_NODE:
               
                logicFilter = this.builder.buildNotFilter();
                break;

            default:
                throw new CQLException(
                        "Expression not supported. And, Or, Not is required",
                        getTokenInPosition(0), this.source);
            }

            return logicFilter;
        } catch (IllegalFilterException ife) {
            throw new CQLException("Exception building LogicFilter",
                    getTokenInPosition(0), ife, this.source);
        }
    }



    /**
     * Creates Binary Spatial Operator
     *
     * @param tipeNode
     *
     * @return BinarySpatialOperator
     * @throws CQLException
     */
    private BinarySpatialOperator buildBinarySpatialOperator(final int nodeType)
            throws CQLException {

        BinarySpatialOperator filter = null;

        switch (nodeType) {
        case JJTROUTINEINVOCATION_GEOOP_EQUAL_NODE:
           
            filter = this.builder.buildSpatialEqualFilter();
            break;
        case JJTROUTINEINVOCATION_GEOOP_DISJOINT_NODE:
            filter = this.builder.buildSpatialDisjointFilter();
            break;
        case JJTROUTINEINVOCATION_GEOOP_INTERSECT_NODE:
            filter = this.builder.buildSpatialIntersectsFilter();
            break;
        case JJTROUTINEINVOCATION_GEOOP_TOUCH_NODE:
            filter = this.builder.buildSpatialTouchesFilter();
            break;

        case JJTROUTINEINVOCATION_GEOOP_CROSS_NODE:
            filter = this.builder.buildSpatialCrossesFilter();
            break;
        case JJTROUTINEINVOCATION_GEOOP_WITHIN_NODE:
            filter = this.builder.buildSpatialWithinFilter();
            break;
        case JJTROUTINEINVOCATION_GEOOP_CONTAIN_NODE:
            filter = this.builder.buildSpatialContainsFilter();
            break;
        case JJTROUTINEINVOCATION_GEOOP_OVERLAP_NODE:
            filter = this.builder.buildSpatialOverlapsFilter();
            break;
        default:
            throw new CQLException("Binary spatial operator unexpected");
        }

        return filter;
    }

    private org.opengis.filter.spatial.BBOX buildBBox(int nodeType)
            throws CQLException {

            if (nodeType == JJTROUTINEINVOCATION_GEOOP_BBOX_SRS_NODE) {
                return this.builder.buildBBoxWithCRS();
            } else {
                return this.builder.buildBBox();
            }
    }

    /**
     * Builds Distance Buffer Operator
     *
     * @param nodeType
     * @return DistanceBufferOperator dwithin and beyond filters
     * @throws CQLException
     */
    private DistanceBufferOperator buildDistanceBufferOperator(
            final int nodeType) throws CQLException {

        DistanceBufferOperator filter = null;

        switch (nodeType) {
        case JJTROUTINEINVOCATION_RELOP_DWITHIN_NODE:
            filter = this.builder.buildSpatialDWithinFilter();
            break;

        case JJTROUTINEINVOCATION_RELOP_BEYOND_NODE:

            filter = this.builder.buildSpatialBeyondFilter();
            break;

        default:
            throw new CQLException("Binary spatial operator unexpected");
        }

        return filter;
    }


    private org.opengis.filter.Filter buildBeforeOrDuring()
            throws CQLException {
        org.opengis.filter.Filter filter = null;

        Result node = this.builder.peekResult();

        switch (node.getNodeType()) {
        case JJTPERIOD_BETWEEN_DATES_NODE:
        case JJTPERIOD_WITH_DATE_DURATION_NODE:
        case JJTPERIOD_WITH_DURATION_DATE_NODE:
            filter = this.builder.buildBeforeOrDuring();
            break;

        default:
            throw new CQLException(
                    "unexpeted date time expression in temporal predicate.",
                    node.getToken(), this.source);
        }

        return filter;
    }

    private Or buildDuringOrAfter()
            throws CQLException {
        Or filter = null;

        Result node = this.builder.peekResult();

        switch (node.getNodeType()) {
        case JJTPERIOD_BETWEEN_DATES_NODE:
        case JJTPERIOD_WITH_DATE_DURATION_NODE:
        case JJTPERIOD_WITH_DURATION_DATE_NODE:
            filter = this.builder.buildDuringOrAfter();

            break;

        default:
            throw new CQLException(
                    "unexpeted date time expression in temporal predicate.",
                    node.getToken(), this.source);
        }

        return filter;
    }





    /**
     * Build the convenient filter for before date and before period filters
     *
     * @param nodeType
     *
     * @return Filter
     * @throws CQLException
     */
    private Before buildBefore()
            throws CQLException {
        Before filter = null;

        // analyzes if the last build is period or date
        Result node = this.builder.peekResult();

        switch (node.getNodeType()) {
        case JJTDATETIME_NODE:
            filter = this.builder.buildBeforeDate();
            break;

        case JJTPERIOD_BETWEEN_DATES_NODE:
        case JJTPERIOD_WITH_DATE_DURATION_NODE:
        case JJTPERIOD_WITH_DURATION_DATE_NODE:
            filter = this.builder.buildBeforePeriod();
            break;

        default:
            throw new CQLException(
                    "unexpeted date time expression in temporal predicate.",
                    node.getToken(), this.source);
        }

        return filter;
    }

    /**
     * Build the convenient filter for during period filters
     *
     * @return Filter
     * @throws CQLException
     */
    private During buildDuring() throws CQLException {
        During filter = null;

        // determines if the node is period or date
        Result node = this.builder.peekResult();

        switch (node.getNodeType()) {
        case JJTPERIOD_BETWEEN_DATES_NODE:
        case JJTPERIOD_WITH_DATE_DURATION_NODE:
        case JJTPERIOD_WITH_DURATION_DATE_NODE:
            filter = this.builder.buildDuringPeriod();
            break;

        default:
            throw new CQLException(
                    "unexpeted period expression in temporal predicate.", node
                            .getToken(), this.source);
        }

        return filter;
    }


    /**
     * build filter for after date and after period
     *
     * @return a filter
     * @throws CQLException
     */
    private After buildAfterPredicate()
            throws CQLException {
        After filter = null;

        // determines if the node is period or date
        Result node = this.builder.peekResult();

        switch (node.getNodeType()) {
        case JJTDATETIME_NODE:
          filter = this.builder.buildAfterDate();
            break;

        case JJTPERIOD_BETWEEN_DATES_NODE:
        case JJTPERIOD_WITH_DURATION_DATE_NODE:
        case JJTPERIOD_WITH_DATE_DURATION_NODE:
            filter = this.builder.buildAfterPeriod();
            break;

        default:
            throw new CQLException(
                    "unexpeted date time expression in temporal predicate.",
                    node.getToken(), this.source);
        }

        return filter;
    }



    /**
     * Builds a compare filter
     *
     * @param filterTipa
     *
     * @return BinaryComparisonOperator
     * @throws CQLException
     */
    private BinaryComparisonOperator buildBinaryComparasionOperator(
            int filterType) throws CQLException {

        switch (filterType) {
        case JJTCOMPARISONPREDICATE_EQ_NODE:
            return this.builder.buildEquals();

        case JJTCOMPARISONPREDICATE_GT_NODE:
            return this.builder.buildGreater();

        case JJTCOMPARISONPREDICATE_LT_NODE:
            return this.builder.buildLess();

        case JJTCOMPARISONPREDICATE_GTE_NODE:
            return this.builder.buildGreaterOrEqual();

        case JJTCOMPARISONPREDICATE_LTE_NODE:
            return this.builder.buildLessOrEqual();

        default:
            throw new CQLException("unexpeted filter type.");
        }
    }
   

   
}
TOP

Related Classes of org.geotools.filter.text.ecql.ECQLCompiler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.