All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.jackrabbit.commons.cnd.CompactNodeTypeDefReader Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.jackrabbit.commons.cnd;

import org.apache.jackrabbit.commons.cnd.DefinitionBuilderFactory.AbstractNodeDefinitionBuilder;
import org.apache.jackrabbit.commons.cnd.DefinitionBuilderFactory.AbstractNodeTypeDefinitionBuilder;
import org.apache.jackrabbit.commons.cnd.DefinitionBuilderFactory.AbstractPropertyDefinitionBuilder;

import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.query.qom.QueryObjectModelConstants;
import javax.jcr.version.OnParentVersionAction;
import java.io.Reader;
import java.util.LinkedList;
import java.util.List;

/**
 * CompactNodeTypeDefReader. Parses node type definitions written in the compact
 * node type definition format and provides a list of type definition
 * objects that can then be used to register node types.
 * 

* The CompactNodeTypeDefReader is parameterizable in the type of the node type * definition T and the type of the namespace mapping N * which the parser should build. For types T and N the * parser's constructor takes a {@link DefinitionBuilderFactory} for * T and N. *

*

* The EBNF grammar of the compact node type definition:
*

 * Cnd ::= {NamespaceMapping | NodeTypeDef}
 * NamespaceMapping ::= '<' Prefix '=' Uri '>'
 * Prefix ::= String
 * Uri ::= String
 * NodeTypeDef ::= NodeTypeName [Supertypes]
 *                 [NodeTypeAttribute {NodeTypeAttribute}]
 *                 {PropertyDef | ChildNodeDef}
 * NodeTypeName ::= '[' String ']'
 * Supertypes ::= '>' (StringList | '?')
 * NodeTypeAttribute ::= Orderable | Mixin | Abstract | Query |
 *                       PrimaryItem
 * Orderable ::= ('orderable' | 'ord' | 'o') ['?']
 * Mixin ::= ('mixin' | 'mix' | 'm') ['?']
 * Abstract ::= ('abstract' | 'abs' | 'a') ['?']
 * Query ::= ('noquery' | 'nq') | ('query' | 'q' )
 * PrimaryItem ::= ('primaryitem'| '!')(String | '?')
 * PropertyDef ::= PropertyName [PropertyType] [DefaultValues]
 *                 [PropertyAttribute {PropertyAttribute}]
 *                 [ValueConstraints]
 * PropertyName ::= '-' String
 * PropertyType ::= '(' ('STRING' | 'BINARY' | 'LONG' | 'DOUBLE' |
 *                       'BOOLEAN' | 'DATE' | 'NAME' | 'PATH' |
 *                       'REFERENCE' | 'WEAKREFERENCE' |
 *                       'DECIMAL' | 'URI' | 'UNDEFINED' | '*' |
 *                       '?') ')'
 * DefaultValues ::= '=' (StringList | '?')
 * ValueConstraints ::= '<' (StringList | '?')
 * ChildNodeDef ::= NodeName [RequiredTypes] [DefaultType]
 *                  [NodeAttribute {NodeAttribute}]
 * NodeName ::= '+' String
 * RequiredTypes ::= '(' (StringList | '?') ')'
 * DefaultType ::= '=' (String | '?')
 * PropertyAttribute ::= Autocreated | Mandatory | Protected |
 *                       Opv | Multiple | QueryOps | NoFullText |
 *                       NoQueryOrder
 * NodeAttribute ::= Autocreated | Mandatory | Protected |
 *                   Opv | Sns
 * Autocreated ::= ('autocreated' | 'aut' | 'a' )['?']
 * Mandatory ::= ('mandatory' | 'man' | 'm') ['?']
 * Protected ::= ('protected' | 'pro' | 'p') ['?']
 * Opv ::= 'COPY' | 'VERSION' | 'INITIALIZE' | 'COMPUTE' |
 *         'IGNORE' | 'ABORT' | ('OPV' '?')
 * Multiple ::= ('multiple' | 'mul' | '*') ['?']
 * QueryOps ::= ('queryops' | 'qop')
 *              (('''Operator {','Operator}''') | '?')
 * Operator ::= '=' | '<>' | '<' | '<=' | '>' | '>=' | 'LIKE'
 * NoFullText ::= ('nofulltext' | 'nof') ['?']
 * NoQueryOrder ::= ('noqueryorder' | 'nqord') ['?']
 * Sns ::= ('sns' | '*') ['?']
 * StringList ::= String {',' String}
 * String ::= QuotedString | UnquotedString
 * QuotedString ::= SingleQuotedString | DoubleQuotedString
 * SingleQuotedString ::= ''' UnquotedString '''
 * DoubleQuotedString ::= '"' UnquotedString '"'
 * UnquotedString ::= XmlChar {XmlChar}
 * XmlChar ::= see 3.2.2 Local Names
 * 
* * @param * @param */ public class CompactNodeTypeDefReader { /** * the list of parsed QNodeTypeDefinition */ private final List nodeTypeDefs = new LinkedList(); /** * the underlying lexer */ private final Lexer lexer; /** * the current token */ private String currentToken; /** * The builder for QNodeTypeDefinitions */ private final DefinitionBuilderFactory factory; /** * Creates a new CND reader and parses the given stream. * * @param r a reader to the CND * @param systemId a informative id of the given stream * @param factory builder for creating new definitions and handling namespaces * @throws ParseException if an error occurs */ public CompactNodeTypeDefReader(Reader r, String systemId, DefinitionBuilderFactory factory) throws ParseException { this(r, systemId, null, factory); } /** * Creates a new CND reader and parses the given stream. * * @param r a reader to the CND * @param systemId a informative id of the given stream * @param nsMapping default namespace mapping to use * @param factory builder for creating new definitions and handling namespaces * @throws ParseException if an error occurs */ public CompactNodeTypeDefReader(Reader r, String systemId, N nsMapping, DefinitionBuilderFactory factory) throws ParseException { super(); this.factory = factory; lexer = new Lexer(r, systemId); if (nsMapping != null) { factory.setNamespaceMapping(nsMapping); } nextToken(); parse(); } /** * Returns the previously assigned system id * * @return the system id */ public String getSystemId() { return lexer.getSystemId(); } /** * Returns the list of parsed node type definitions definitions. * * @return a collection of node type definition objects */ public List getNodeTypeDefinitions() { return nodeTypeDefs; } /** * Returns the namespace mapping. * * @return */ public N getNamespaceMapping() { return factory.getNamespaceMapping(); } /** * Parses the definition * * @throws ParseException if an error during parsing occurs */ private void parse() throws ParseException { while (!currentTokenEquals(Lexer.EOF)) { if (!doNameSpace()) { break; } } try { while (!currentTokenEquals(Lexer.EOF)) { AbstractNodeTypeDefinitionBuilder ntd = factory.newNodeTypeDefinitionBuilder(); ntd.setOrderableChildNodes(false); ntd.setMixin(false); ntd.setAbstract(false); ntd.setQueryable(true); doNodeTypeName(ntd); doSuperTypes(ntd); doOptions(ntd); doItemDefs(ntd); nodeTypeDefs.add(ntd.build()); } } catch (RepositoryException e) { lexer.fail(e); } } /** * processes the namespace declaration * * @return true if a namespace was parsed * @throws ParseException if an error during parsing occurs */ private boolean doNameSpace() throws ParseException { if (!currentTokenEquals('<')) { return false; } nextToken(); String prefix = currentToken; nextToken(); if (!currentTokenEquals('=')) { lexer.fail("Missing = in namespace decl."); } nextToken(); String uri = currentToken; nextToken(); if (!currentTokenEquals('>')) { lexer.fail("Missing > in namespace decl."); } try { factory.setNamespace(prefix, uri); } catch (RepositoryException e) { lexer.fail("Error setting namespace mapping " + currentToken, e); } nextToken(); return true; } /** * processes the nodetype name * * @param ntd nodetype definition builder * @throws ParseException if an error during parsing occurs */ private void doNodeTypeName(AbstractNodeTypeDefinitionBuilder ntd) throws ParseException { if (!currentTokenEquals(Lexer.BEGIN_NODE_TYPE_NAME)) { lexer.fail("Missing '" + Lexer.BEGIN_NODE_TYPE_NAME + "' delimiter for beginning of node type name"); } nextToken(); try { ntd.setName(currentToken); } catch (RepositoryException e) { lexer.fail("Error setting node type name " + currentToken, e); } nextToken(); if (!currentTokenEquals(Lexer.END_NODE_TYPE_NAME)) { lexer.fail("Missing '" + Lexer.END_NODE_TYPE_NAME + "' delimiter for end of node type name, found " + currentToken); } nextToken(); } /** * processes the superclasses * * @param ntd nodetype definition builder * @throws ParseException if an error during parsing occurs */ private void doSuperTypes(AbstractNodeTypeDefinitionBuilder ntd) throws ParseException { if (currentTokenEquals(Lexer.EXTENDS)) do { nextToken(); try { ntd.addSupertype(currentToken); } catch (RepositoryException e) { lexer.fail("Error setting super type of " + ntd.getName() + " to " + currentToken, e); } nextToken(); } while (currentTokenEquals(Lexer.LIST_DELIMITER)); } /** * processes the options * * @param ntd nodetype definition builder * @throws ParseException if an error during parsing occurs */ private void doOptions(AbstractNodeTypeDefinitionBuilder ntd) throws ParseException { boolean hasOption = true; try { while (hasOption) { if (currentTokenEquals(Lexer.ORDERABLE)) { nextToken(); ntd.setOrderableChildNodes(true); } else if (currentTokenEquals(Lexer.MIXIN)) { nextToken(); ntd.setMixin(true); } else if (currentTokenEquals(Lexer.ABSTRACT)) { nextToken(); ntd.setAbstract(true); } else if (currentTokenEquals(Lexer.NOQUERY)) { nextToken(); ntd.setQueryable(false); } else if (currentTokenEquals(Lexer.QUERY)) { nextToken(); ntd.setQueryable(true); } else if (currentTokenEquals(Lexer.PRIMARYITEM)) { nextToken(); ntd.setPrimaryItemName(currentToken); nextToken(); } else { hasOption = false; } } } catch (RepositoryException e) { lexer.fail("Error setting option of " + ntd.getName() + " to " + currentToken, e); } } /** * processes the item definitions * * @param ntd nodetype definition builder * @throws ParseException if an error during parsing occurs */ private void doItemDefs(AbstractNodeTypeDefinitionBuilder ntd) throws ParseException { while (currentTokenEquals(Lexer.PROPERTY_DEFINITION) || currentTokenEquals(Lexer.CHILD_NODE_DEFINITION)) { if (currentTokenEquals(Lexer.PROPERTY_DEFINITION)) { try { AbstractPropertyDefinitionBuilder pd = ntd.newPropertyDefinitionBuilder(); try { pd.setAutoCreated(false); pd.setDeclaringNodeType(ntd.getName()); pd.setMandatory(false); pd.setMultiple(false); pd.setOnParentVersion(OnParentVersionAction.COPY); pd.setProtected(false); pd.setRequiredType(PropertyType.STRING); pd.setFullTextSearchable(true); pd.setQueryOrderable(true); } catch (RepositoryException e) { lexer.fail("Error setting property definitions of " + pd.getName() + " to " + currentToken, e); } nextToken(); doPropertyDefinition(pd, ntd); pd.build(); } catch (RepositoryException e) { lexer.fail("Error building property definition for " + ntd.getName(), e); } } else if (currentTokenEquals(Lexer.CHILD_NODE_DEFINITION)) { try { AbstractNodeDefinitionBuilder nd = ntd.newNodeDefinitionBuilder(); try { nd.setAllowsSameNameSiblings(false); nd.setAutoCreated(false); nd.setDeclaringNodeType(ntd.getName()); nd.setMandatory(false); nd.setOnParentVersion(OnParentVersionAction.COPY); nd.setProtected(false); } catch (RepositoryException e) { lexer.fail("Error setting node definitions of " + nd.getName() + " to " + currentToken, e); } nextToken(); doChildNodeDefinition(nd, ntd); nd.build(); } catch (RepositoryException e) { lexer.fail("Error building node definition for " + ntd.getName(), e); } } } } /** * processes the property definition * * @param pd property definition builder * @param ntd declaring nodetype definition builder * @throws ParseException if an error during parsing occur */ private void doPropertyDefinition(AbstractPropertyDefinitionBuilder pd, AbstractNodeTypeDefinitionBuilder ntd) throws ParseException { try { pd.setName(currentToken); } catch (RepositoryException e) { lexer.fail("Invalid property name '" + currentToken, e); } nextToken(); doPropertyType(pd); doPropertyDefaultValue(pd); doPropertyAttributes(pd, ntd); doPropertyValueConstraints(pd); } /** * processes the property type * * @param pd property definition builder * @throws ParseException if an error during parsing occurs */ private void doPropertyType(AbstractPropertyDefinitionBuilder pd) throws ParseException { if (!currentTokenEquals(Lexer.BEGIN_TYPE)) { return; } nextToken(); try { if (currentTokenEquals(Lexer.STRING)) { pd.setRequiredType(PropertyType.STRING); } else if (currentTokenEquals(Lexer.BINARY)) { pd.setRequiredType(PropertyType.BINARY); } else if (currentTokenEquals(Lexer.LONG)) { pd.setRequiredType(PropertyType.LONG); } else if (currentTokenEquals(Lexer.DECIMAL)) { pd.setRequiredType(PropertyType.DECIMAL); } else if (currentTokenEquals(Lexer.DOUBLE)) { pd.setRequiredType(PropertyType.DOUBLE); } else if (currentTokenEquals(Lexer.BOOLEAN)) { pd.setRequiredType(PropertyType.BOOLEAN); } else if (currentTokenEquals(Lexer.DATE)) { pd.setRequiredType(PropertyType.DATE); } else if (currentTokenEquals(Lexer.NAME)) { pd.setRequiredType(PropertyType.NAME); } else if (currentTokenEquals(Lexer.PATH)) { pd.setRequiredType(PropertyType.PATH); } else if (currentTokenEquals(Lexer.URI)) { pd.setRequiredType(PropertyType.URI); } else if (currentTokenEquals(Lexer.REFERENCE)) { pd.setRequiredType(PropertyType.REFERENCE); } else if (currentTokenEquals(Lexer.WEAKREFERENCE)) { pd.setRequiredType(PropertyType.WEAKREFERENCE); } else if (currentTokenEquals(Lexer.UNDEFINED)) { pd.setRequiredType(PropertyType.UNDEFINED); } else { lexer.fail("Unkown property type '" + currentToken + "' specified"); } } catch (RepositoryException e) { lexer.fail("Error setting property type of " + pd.getName() + " to " + currentToken, e); } nextToken(); if (!currentTokenEquals(Lexer.END_TYPE)) { lexer.fail("Missing '" + Lexer.END_TYPE + "' delimiter for end of property type"); } nextToken(); } /** * processes the property attributes * * @param pd property definition builder * @param ntd declaring nodetype definition builder * @throws ParseException if an error during parsing occurs */ private void doPropertyAttributes(AbstractPropertyDefinitionBuilder pd, AbstractNodeTypeDefinitionBuilder ntd) throws ParseException { try { while (currentTokenEquals(Lexer.PROP_ATTRIBUTE)) { if (currentTokenEquals(Lexer.PRIMARY)) { ntd.setPrimaryItemName(pd.getName()); } else if (currentTokenEquals(Lexer.AUTOCREATED)) { pd.setAutoCreated(true); } else if (currentTokenEquals(Lexer.MANDATORY)) { pd.setMandatory(true); } else if (currentTokenEquals(Lexer.PROTECTED)) { pd.setProtected(true); } else if (currentTokenEquals(Lexer.MULTIPLE)) { pd.setMultiple(true); } else if (currentTokenEquals(Lexer.COPY)) { pd.setOnParentVersion(OnParentVersionAction.COPY); } else if (currentTokenEquals(Lexer.VERSION)) { pd.setOnParentVersion(OnParentVersionAction.VERSION); } else if (currentTokenEquals(Lexer.INITIALIZE)) { pd.setOnParentVersion(OnParentVersionAction.INITIALIZE); } else if (currentTokenEquals(Lexer.COMPUTE)) { pd.setOnParentVersion(OnParentVersionAction.COMPUTE); } else if (currentTokenEquals(Lexer.IGNORE)) { pd.setOnParentVersion(OnParentVersionAction.IGNORE); } else if (currentTokenEquals(Lexer.ABORT)) { pd.setOnParentVersion(OnParentVersionAction.ABORT); } else if (currentTokenEquals(Lexer.NOFULLTEXT)) { pd.setFullTextSearchable(false); } else if (currentTokenEquals(Lexer.NOQUERYORDER)) { pd.setQueryOrderable(false); } else if (currentTokenEquals(Lexer.QUERYOPS)) { doPropertyQueryOperators(pd); } nextToken(); } } catch (RepositoryException e) { lexer.fail("Error setting property attribute of " + pd.getName() + " to " + currentToken, e); } } /** * processes the property query operators * * @param pd the property definition builder * @throws ParseException if an error occurs */ private void doPropertyQueryOperators(AbstractPropertyDefinitionBuilder pd) throws ParseException { if (!currentTokenEquals(Lexer.QUERYOPS)) { return; } nextToken(); String[] ops = currentToken.split(","); List queryOps = new LinkedList(); for (String op : ops) { String s = op.trim(); if (s.equals(Lexer.QUEROPS_EQUAL)) { queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO); } else if (s.equals(Lexer.QUEROPS_NOTEQUAL)) { queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_NOT_EQUAL_TO); } else if (s.equals(Lexer.QUEROPS_LESSTHAN)) { queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN); } else if (s.equals(Lexer.QUEROPS_LESSTHANOREQUAL)) { queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO); } else if (s.equals(Lexer.QUEROPS_GREATERTHAN)) { queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN); } else if (s.equals(Lexer.QUEROPS_GREATERTHANOREQUAL)) { queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO); } else if (s.equals(Lexer.QUEROPS_LIKE)) { queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_LIKE); } else { lexer.fail("'" + s + "' is not a valid query operator"); } } try { pd.setAvailableQueryOperators(queryOps.toArray(new String[queryOps.size()])); } catch (RepositoryException e) { lexer.fail("Error query operators for " + pd.getName() + " to " + currentToken, e); } } /** * processes the property default values * * @param pd property definition builder * @throws ParseException if an error during parsing occurs */ private void doPropertyDefaultValue(AbstractPropertyDefinitionBuilder pd) throws ParseException { if (!currentTokenEquals(Lexer.DEFAULT)) { return; } do { nextToken(); try { pd.addDefaultValues(currentToken); } catch (RepositoryException e) { lexer.fail("Error adding default value for " + pd.getName() + " to " + currentToken, e); } nextToken(); } while (currentTokenEquals(Lexer.LIST_DELIMITER)); } /** * processes the property value constraints * * @param pd property definition builder * @throws ParseException if an error during parsing occurs */ private void doPropertyValueConstraints(AbstractPropertyDefinitionBuilder pd) throws ParseException { if (!currentTokenEquals(Lexer.CONSTRAINT)) { return; } do { nextToken(); try { pd.addValueConstraint(currentToken); } catch (RepositoryException e) { lexer.fail("Error adding value constraint for " + pd.getName() + " to " + currentToken, e); } nextToken(); } while (currentTokenEquals(Lexer.LIST_DELIMITER)); } /** * processes the childnode definition * * @param nd node definition builder * @param ntd declaring nodetype definition builder * @throws ParseException if an error during parsing occurs */ private void doChildNodeDefinition(AbstractNodeDefinitionBuilder nd, AbstractNodeTypeDefinitionBuilder ntd) throws ParseException { try { nd.setName(currentToken); } catch (RepositoryException e) { lexer.fail("Invalid child node name '" + currentToken, e); } nextToken(); doChildNodeRequiredTypes(nd); doChildNodeDefaultType(nd); doChildNodeAttributes(nd, ntd); } /** * processes the childnode required types * * @param nd node definition builder * @throws ParseException if an error during parsing occurs */ private void doChildNodeRequiredTypes(AbstractNodeDefinitionBuilder nd) throws ParseException { if (!currentTokenEquals(Lexer.BEGIN_TYPE)) { return; } do { nextToken(); try { nd.addRequiredPrimaryType(currentToken); } catch (RepositoryException e) { lexer.fail("Error setting required primary type of " + nd.getName() + " to " + currentToken, e); } nextToken(); } while (currentTokenEquals(Lexer.LIST_DELIMITER)); nextToken(); } /** * processes the childnode default types * * @param nd node definition builder * @throws ParseException if an error during parsing occurs */ private void doChildNodeDefaultType(AbstractNodeDefinitionBuilder nd) throws ParseException { if (!currentTokenEquals(Lexer.DEFAULT)) { return; } nextToken(); try { nd.setDefaultPrimaryType(currentToken); } catch (RepositoryException e) { lexer.fail("Error setting default primary type of " + nd.getName() + " to " + currentToken, e); } nextToken(); } /** * processes the childnode attributes * * @param nd node definition builder * @param ntd declaring nodetype definition builder * @throws ParseException if an error during parsing occurs */ private void doChildNodeAttributes(AbstractNodeDefinitionBuilder nd, AbstractNodeTypeDefinitionBuilder ntd) throws ParseException { try { while (currentTokenEquals(Lexer.NODE_ATTRIBUTE)) { if (currentTokenEquals(Lexer.PRIMARY)) { ntd.setPrimaryItemName(nd.getName()); } else if (currentTokenEquals(Lexer.AUTOCREATED)) { nd.setAutoCreated(true); } else if (currentTokenEquals(Lexer.MANDATORY)) { nd.setMandatory(true); } else if (currentTokenEquals(Lexer.PROTECTED)) { nd.setProtected(true); } else if (currentTokenEquals(Lexer.SNS)) { nd.setAllowsSameNameSiblings(true); } else if (currentTokenEquals(Lexer.COPY)) { nd.setOnParentVersion(OnParentVersionAction.COPY); } else if (currentTokenEquals(Lexer.VERSION)) { nd.setOnParentVersion(OnParentVersionAction.VERSION); } else if (currentTokenEquals(Lexer.INITIALIZE)) { nd.setOnParentVersion(OnParentVersionAction.INITIALIZE); } else if (currentTokenEquals(Lexer.COMPUTE)) { nd.setOnParentVersion(OnParentVersionAction.COMPUTE); } else if (currentTokenEquals(Lexer.IGNORE)) { nd.setOnParentVersion(OnParentVersionAction.IGNORE); } else if (currentTokenEquals(Lexer.ABORT)) { nd.setOnParentVersion(OnParentVersionAction.ABORT); } nextToken(); } } catch (RepositoryException e) { lexer.fail("Error setting child node attribute of " + nd.getName() + " to " + currentToken, e); } } /** * Gets the next token from the underlying lexer. * * @throws ParseException if the lexer fails to get the next token. * @see Lexer#getNextToken() */ private void nextToken() throws ParseException { currentToken = lexer.getNextToken(); } /** * Checks if the {@link #currentToken} is semantically equal to the given * argument ignoring the case. * * @param s the tokens to compare with * @return true if equals; false otherwise. */ private boolean currentTokenEquals(String[] s) { for (String value : s) { if (currentToken.equalsIgnoreCase(value)) { return true; } } return false; } /** * Checks if the {@link #currentToken} is semantically equal to the given * argument. * * @param c the tokens to compare with * @return true if equals; false otherwise. */ private boolean currentTokenEquals(char c) { return currentToken.length() == 1 && currentToken.charAt(0) == c; } /** * Checks if the {@link #currentToken} is semantically equal to the given * argument. * * @param s the tokens to compare with * @return true if equals; false otherwise. */ private boolean currentTokenEquals(String s) { return currentToken.equals(s); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy