| /* |
| * Copyright 2004-2005 The Apache Software Foundation or its licensors, |
| * as applicable. |
| * |
| * Licensed 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.core.nodetype.compact; |
| |
| import org.apache.jackrabbit.core.nodetype.NodeTypeDef; |
| import org.apache.jackrabbit.core.nodetype.PropDefImpl; |
| import org.apache.jackrabbit.core.nodetype.NodeDefImpl; |
| import org.apache.jackrabbit.core.nodetype.ItemDef; |
| import org.apache.jackrabbit.core.nodetype.NodeDef; |
| import org.apache.jackrabbit.core.nodetype.PropDef; |
| import org.apache.jackrabbit.core.nodetype.ValueConstraint; |
| import org.apache.jackrabbit.core.nodetype.InvalidConstraintException; |
| import org.apache.jackrabbit.core.value.InternalValue; |
| import org.apache.jackrabbit.name.QName; |
| import org.apache.jackrabbit.name.NoPrefixDeclaredException; |
| import org.apache.jackrabbit.name.IllegalNameException; |
| import org.apache.jackrabbit.name.UnknownPrefixException; |
| import org.apache.jackrabbit.util.name.NamespaceMapping; |
| |
| import javax.jcr.NamespaceException; |
| import javax.jcr.PropertyType; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.ValueFormatException; |
| import javax.jcr.version.OnParentVersionAction; |
| import java.io.Reader; |
| import java.util.ArrayList; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.HashSet; |
| import java.util.Arrays; |
| |
| /** |
| * CompactNodeTypeDefReader. Parses node type definitions written in the compact |
| * node type definition format and returns a list of NodeTypeDef objects that |
| * can then be used to register node types. |
| * <p/> |
| * The BNF grammar of the compact node type definition:<br> |
| * <pre> |
| * cnd ::= {ns_mapping | node_type_def} |
| * |
| * ns_mapping ::= "<" prefix "=" namespace ">" |
| * |
| * prefix ::= string |
| * |
| * namespace ::= string |
| * |
| * node_type_def ::= node_type_name [super_types] [options] {property_def | node_def} |
| * |
| * node_type_name ::= "[" string "]" |
| * |
| * super_types ::= ">" string_list |
| * |
| * options ::= orderable_opt | mixin_opt | orderable_opt mixin_opt | mixin_opt orderable_opt |
| * |
| * orderable_opt ::= "orderable" | "ord" | "o" |
| * |
| * mixin_opt ::= "mixin" | "mix" | "m" |
| * |
| * property_def ::= "-" property_name [property_type_decl] [default_values] [attributes] [value_constraints] |
| * |
| * property_name ::= string |
| * |
| * property_type_decl ::= "(" property_type ")" |
| * |
| * property_type ::= "STRING" | "String |"string" | |
| * "BINARY" | "Binary" | "binary" | |
| * "LONG" | "Long" | "long" | |
| * "DOUBLE" | "Double" | "double" | |
| * "BOOLEAN" | "Boolean" | "boolean" | |
| * "DATE" | "Date" | "date" | |
| * "NAME | "Name | "name | |
| * "PATH" | "Path" | "path" | |
| * "REFERENCE" | "Reference" | "reference" | |
| * "UNDEFINED" | "Undefined" | "undefined" | "*" |
| * |
| * |
| * default_values ::= "=" string_list |
| * |
| * value_constraints ::= "<" string_list |
| * |
| * node_def ::= "+" node_name [required_types] [default_type] [attributes] |
| * |
| * node_name ::= string |
| * |
| * required_types ::= "(" string_list ")" |
| * |
| * default_type ::= "=" string |
| * |
| * attributes ::= "primary" | "pri" | "!" | |
| * "autocreated" | "aut" | "a" | |
| * "mandatory" | "man" | "m" | |
| * "protected" | "pro" | "p" | |
| * "multiple" | "mul" | "*" | |
| * "COPY" | "Copy" | "copy" | |
| * "VERSION" | "Version" | "version" | |
| * "INITIALIZE" | "Initialize" | "initialize" | |
| * "COMPUTE" | "Compute" | "compute" | |
| * "IGNORE" | "Ignore" | "ignore" | |
| * "ABORT" | "Abort" | "abort" |
| * |
| * string_list ::= string {"," string} |
| * |
| * string ::= quoted_string | unquoted_string |
| * |
| * quoted_string :: = "'" unquoted_string "'" |
| * |
| * unquoted_string ::= ...a string... |
| * </pre> |
| */ |
| public class CompactNodeTypeDefReader { |
| |
| /** |
| * the list of parsed nodetype defs |
| */ |
| private List nodeTypeDefs = new LinkedList(); |
| |
| /** |
| * the current namespace mapping |
| */ |
| private NamespaceMapping nsMapping; |
| |
| /** |
| * the underlying lexer |
| */ |
| private Lexer lexer; |
| |
| /** |
| * the current token |
| */ |
| private String currentToken; |
| |
| /** |
| * Creates a new CND reader. |
| * |
| * @param r |
| * @throws ParseException |
| */ |
| public CompactNodeTypeDefReader(Reader r, String systemId) throws ParseException { |
| this(r, systemId, new NamespaceMapping()); |
| } |
| |
| |
| /** |
| * Creates a new CND reader. |
| * |
| * @param r |
| * @throws ParseException |
| */ |
| public CompactNodeTypeDefReader(Reader r, String systemId, NamespaceMapping mapping) |
| throws ParseException { |
| lexer = new Lexer(r, systemId); |
| this.nsMapping = mapping; |
| nextToken(); |
| parse(); |
| } |
| |
| /** |
| * Returns the list of parsed nodetype definitions. |
| * |
| * @return a List of NodeTypeDef objects |
| */ |
| public List getNodeTypeDefs() { |
| return nodeTypeDefs; |
| } |
| |
| /** |
| * Returns the namespace mapping. |
| * |
| * @return a NamespaceMapping object. |
| */ |
| public NamespaceMapping getNamespaceMapping() { |
| return nsMapping; |
| } |
| |
| /** |
| * Parses the definition |
| * |
| * @throws ParseException |
| */ |
| private void parse() throws ParseException { |
| while (!currentTokenEquals(Lexer.EOF)) { |
| if (doNameSpace()) { |
| continue; |
| } |
| NodeTypeDef ntd = new NodeTypeDef(); |
| ntd.setOrderableChildNodes(false); |
| ntd.setMixin(false); |
| ntd.setPrimaryItemName(null); |
| doNodeTypeName(ntd); |
| doSuperClasses(ntd); |
| doOptions(ntd); |
| // add nt:base to superclasses if not mixin |
| if (!ntd.isMixin()) { |
| HashSet superTypes = new HashSet(Arrays.asList(ntd.getSupertypes())); |
| if (!superTypes.contains(QName.NT_BASE)) { |
| superTypes.add(QName.NT_BASE); |
| ntd.setSupertypes((QName[]) superTypes.toArray(new QName[superTypes.size()])); |
| } |
| } |
| doItemDefs(ntd); |
| nodeTypeDefs.add(ntd); |
| } |
| } |
| |
| /** |
| * processes the namespace declaration |
| * |
| * @return |
| * @throws ParseException |
| */ |
| 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 { |
| nsMapping.setMapping(prefix, uri); |
| } catch (NamespaceException e) { |
| // ignore |
| } |
| nextToken(); |
| return true; |
| } |
| |
| /** |
| * processes the nodetype name |
| * |
| * @param ntd |
| * @throws ParseException |
| */ |
| private void doNodeTypeName(NodeTypeDef 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(); |
| ntd.setName(toQName(currentToken)); |
| |
| nextToken(); |
| if (!currentTokenEquals(Lexer.END_NODE_TYPE_NAME)) { |
| lexer.fail("Missing '" + Lexer.END_NODE_TYPE_NAME + "' delimiter for end of node type name"); |
| } |
| nextToken(); |
| } |
| |
| /** |
| * processes the superclasses |
| * |
| * @param ntd |
| * @throws ParseException |
| */ |
| private void doSuperClasses(NodeTypeDef ntd) throws ParseException { |
| List supertypes = new ArrayList(); |
| if (!currentTokenEquals(Lexer.EXTENDS)) { |
| return; |
| } |
| do { |
| nextToken(); |
| supertypes.add(toQName(currentToken)); |
| nextToken(); |
| } while (currentTokenEquals(Lexer.LIST_DELIMITER)); |
| ntd.setSupertypes((QName[]) supertypes.toArray(new QName[0])); |
| } |
| |
| /** |
| * processes the options |
| * |
| * @param ntd |
| * @throws ParseException |
| */ |
| private void doOptions(NodeTypeDef ntd) throws ParseException { |
| if (currentTokenEquals(Lexer.ORDERABLE)) { |
| ntd.setOrderableChildNodes(true); |
| nextToken(); |
| if (currentTokenEquals(Lexer.MIXIN)) { |
| ntd.setMixin(true); |
| nextToken(); |
| } |
| } else if (currentTokenEquals(Lexer.MIXIN)) { |
| ntd.setMixin(true); |
| nextToken(); |
| if (currentTokenEquals(Lexer.ORDERABLE)) { |
| ntd.setMixin(true); |
| nextToken(); |
| } |
| } |
| } |
| |
| /** |
| * processes the item definitions |
| * |
| * @param ntd |
| * @throws ParseException |
| */ |
| private void doItemDefs(NodeTypeDef ntd) throws ParseException { |
| List propertyDefinitions = new ArrayList(); |
| List nodeDefinitions = new ArrayList(); |
| while (currentTokenEquals(Lexer.PROPERTY_DEFINITION) || currentTokenEquals(Lexer.CHILD_NODE_DEFINITION)) { |
| if (currentTokenEquals(Lexer.PROPERTY_DEFINITION)) { |
| PropDefImpl pdi = new PropDefImpl(); |
| |
| pdi.setAutoCreated(false); |
| pdi.setDeclaringNodeType(ntd.getName()); |
| pdi.setDefaultValues(null); |
| pdi.setMandatory(false); |
| pdi.setMultiple(false); |
| pdi.setOnParentVersion(OnParentVersionAction.COPY); |
| pdi.setProtected(false); |
| pdi.setRequiredType(PropertyType.STRING); |
| pdi.setValueConstraints(null); |
| |
| nextToken(); |
| doPropertyDefinition(pdi, ntd); |
| propertyDefinitions.add(pdi); |
| |
| } else if (currentTokenEquals(Lexer.CHILD_NODE_DEFINITION)) { |
| NodeDefImpl ndi = new NodeDefImpl(); |
| |
| ndi.setAllowsSameNameSiblings(false); |
| ndi.setAutoCreated(false); |
| ndi.setDeclaringNodeType(ntd.getName()); |
| ndi.setMandatory(false); |
| ndi.setOnParentVersion(OnParentVersionAction.COPY); |
| ndi.setProtected(false); |
| ndi.setDefaultPrimaryType(null); |
| ndi.setRequiredPrimaryTypes(new QName[]{QName.NT_BASE}); |
| |
| nextToken(); |
| doChildNodeDefinition(ndi, ntd); |
| nodeDefinitions.add(ndi); |
| } |
| } |
| |
| if (propertyDefinitions.size() > 0) { |
| ntd.setPropertyDefs((PropDef[]) propertyDefinitions.toArray(new PropDef[0])); |
| } |
| |
| if (nodeDefinitions.size() > 0) { |
| ntd.setChildNodeDefs((NodeDef[]) nodeDefinitions.toArray(new NodeDef[0])); |
| } |
| } |
| |
| /** |
| * processes the property definition |
| * |
| * @param pdi |
| * @param ntd |
| * @throws ParseException |
| */ |
| private void doPropertyDefinition(PropDefImpl pdi, NodeTypeDef ntd) |
| throws ParseException { |
| if (currentToken.equals("*")) { |
| pdi.setName(ItemDef.ANY_NAME); |
| } else { |
| pdi.setName(toQName(currentToken)); |
| } |
| nextToken(); |
| doPropertyType(pdi); |
| doPropertyDefaultValue(pdi); |
| doPropertyAttributes(pdi, ntd); |
| doPropertyValueConstraints(pdi); |
| } |
| |
| /** |
| * processes the property type |
| * |
| * @param pdi |
| * @throws ParseException |
| */ |
| private void doPropertyType(PropDefImpl pdi) throws ParseException { |
| if (!currentTokenEquals(Lexer.BEGIN_TYPE)) { |
| return; |
| } |
| nextToken(); |
| if (currentTokenEquals(Lexer.STRING)) { |
| pdi.setRequiredType(PropertyType.STRING); |
| } else if (currentTokenEquals(Lexer.BINARY)) { |
| pdi.setRequiredType(PropertyType.BINARY); |
| } else if (currentTokenEquals(Lexer.LONG)) { |
| pdi.setRequiredType(PropertyType.LONG); |
| } else if (currentTokenEquals(Lexer.DOUBLE)) { |
| pdi.setRequiredType(PropertyType.DOUBLE); |
| } else if (currentTokenEquals(Lexer.BOOLEAN)) { |
| pdi.setRequiredType(PropertyType.BOOLEAN); |
| } else if (currentTokenEquals(Lexer.DATE)) { |
| pdi.setRequiredType(PropertyType.DATE); |
| } else if (currentTokenEquals(Lexer.NAME)) { |
| pdi.setRequiredType(PropertyType.NAME); |
| } else if (currentTokenEquals(Lexer.PATH)) { |
| pdi.setRequiredType(PropertyType.PATH); |
| } else if (currentTokenEquals(Lexer.REFERENCE)) { |
| pdi.setRequiredType(PropertyType.REFERENCE); |
| } else if (currentTokenEquals(Lexer.UNDEFINED)) { |
| pdi.setRequiredType(PropertyType.UNDEFINED); |
| } else { |
| lexer.fail("Unkown property type '" + currentToken + "' specified"); |
| } |
| nextToken(); |
| if (!currentTokenEquals(Lexer.END_TYPE)) { |
| lexer.fail("Missing '" + Lexer.END_TYPE + "' delimiter for end of property type"); |
| } |
| nextToken(); |
| } |
| |
| /** |
| * processes the property attributes |
| * |
| * @param pdi |
| * @param ntd |
| * @throws ParseException |
| */ |
| private void doPropertyAttributes(PropDefImpl pdi, NodeTypeDef ntd) throws ParseException { |
| while (currentTokenEquals(Lexer.ATTRIBUTE)) { |
| if (currentTokenEquals(Lexer.PRIMARY)) { |
| if (ntd.getPrimaryItemName() != null) { |
| String name = null; |
| try { |
| name = nsMapping.getJCRName(ntd.getName()); |
| } catch (NoPrefixDeclaredException e) { |
| // Should never happen, checked earlier |
| } |
| lexer.fail("More than one primary item specified in node type '" + name + "'"); |
| } |
| ntd.setPrimaryItemName(pdi.getName()); |
| } else if (currentTokenEquals(Lexer.AUTOCREATED)) { |
| pdi.setAutoCreated(true); |
| } else if (currentTokenEquals(Lexer.MANDATORY)) { |
| pdi.setMandatory(true); |
| } else if (currentTokenEquals(Lexer.PROTECTED)) { |
| pdi.setProtected(true); |
| } else if (currentTokenEquals(Lexer.MULTIPLE)) { |
| pdi.setMultiple(true); |
| } else if (currentTokenEquals(Lexer.COPY)) { |
| pdi.setOnParentVersion(OnParentVersionAction.COPY); |
| } else if (currentTokenEquals(Lexer.VERSION)) { |
| pdi.setOnParentVersion(OnParentVersionAction.VERSION); |
| } else if (currentTokenEquals(Lexer.INITIALIZE)) { |
| pdi.setOnParentVersion(OnParentVersionAction.INITIALIZE); |
| } else if (currentTokenEquals(Lexer.COMPUTE)) { |
| pdi.setOnParentVersion(OnParentVersionAction.COMPUTE); |
| } else if (currentTokenEquals(Lexer.IGNORE)) { |
| pdi.setOnParentVersion(OnParentVersionAction.IGNORE); |
| } else if (currentTokenEquals(Lexer.ABORT)) { |
| pdi.setOnParentVersion(OnParentVersionAction.ABORT); |
| } |
| nextToken(); |
| } |
| } |
| |
| /** |
| * processes the property default values |
| * |
| * @param pdi |
| * @throws ParseException |
| */ |
| private void doPropertyDefaultValue(PropDefImpl pdi) throws ParseException { |
| if (!currentTokenEquals(Lexer.DEFAULT)) { |
| return; |
| } |
| List defaultValues = new ArrayList(); |
| do { |
| nextToken(); |
| InternalValue value = null; |
| try { |
| value = InternalValue.create(currentToken, pdi.getRequiredType(), nsMapping); |
| } catch (ValueFormatException e) { |
| lexer.fail("'" + currentToken + "' is not a valid string representation of a value of type " + pdi.getRequiredType()); |
| } catch (RepositoryException e) { |
| lexer.fail("An error occured during value conversion of '" + currentToken + "'"); |
| } |
| defaultValues.add(value); |
| nextToken(); |
| } while (currentTokenEquals(Lexer.LIST_DELIMITER)); |
| pdi.setDefaultValues((InternalValue[]) defaultValues.toArray(new InternalValue[0])); |
| } |
| |
| /** |
| * processes the property value constraints |
| * |
| * @param pdi |
| * @throws ParseException |
| */ |
| private void doPropertyValueConstraints(PropDefImpl pdi) throws ParseException { |
| if (!currentTokenEquals(Lexer.CONSTRAINT)) { |
| return; |
| } |
| List constraints = new ArrayList(); |
| do { |
| nextToken(); |
| ValueConstraint constraint = null; |
| try { |
| constraint = ValueConstraint.create(pdi.getRequiredType(), currentToken, nsMapping); |
| } catch (InvalidConstraintException e) { |
| lexer.fail("'" + currentToken + "' is not a valid constraint expression for a value of type " + pdi.getRequiredType()); |
| } |
| constraints.add(constraint); |
| nextToken(); |
| } while (currentTokenEquals(Lexer.LIST_DELIMITER)); |
| pdi.setValueConstraints((ValueConstraint[]) constraints.toArray(new ValueConstraint[0])); |
| } |
| |
| /** |
| * processes the childnode definition |
| * |
| * @param ndi |
| * @param ntd |
| * @throws ParseException |
| */ |
| private void doChildNodeDefinition(NodeDefImpl ndi, NodeTypeDef ntd) |
| throws ParseException { |
| if (currentTokenEquals('*')) { |
| ndi.setName(ItemDef.ANY_NAME); |
| } else { |
| ndi.setName(toQName(currentToken)); |
| } |
| nextToken(); |
| doChildNodeRequiredTypes(ndi); |
| doChildNodeDefaultType(ndi); |
| doChildNodeAttributes(ndi, ntd); |
| } |
| |
| /** |
| * processes the childnode required types |
| * |
| * @param ndi |
| * @throws ParseException |
| */ |
| private void doChildNodeRequiredTypes(NodeDefImpl ndi) throws ParseException { |
| if (!currentTokenEquals(Lexer.BEGIN_TYPE)) { |
| return; |
| } |
| List types = new ArrayList(); |
| do { |
| nextToken(); |
| types.add(toQName(currentToken)); |
| nextToken(); |
| } while (currentTokenEquals(Lexer.LIST_DELIMITER)); |
| ndi.setRequiredPrimaryTypes((QName[]) types.toArray(new QName[0])); |
| nextToken(); |
| } |
| |
| /** |
| * processes the childnode default types |
| * |
| * @param ndi |
| * @throws ParseException |
| */ |
| private void doChildNodeDefaultType(NodeDefImpl ndi) throws ParseException { |
| if (!currentTokenEquals(Lexer.DEFAULT)) { |
| return; |
| } |
| nextToken(); |
| ndi.setDefaultPrimaryType(toQName(currentToken)); |
| nextToken(); |
| } |
| |
| /** |
| * processes the childnode attributes |
| * |
| * @param ndi |
| * @param ntd |
| * @throws ParseException |
| */ |
| private void doChildNodeAttributes(NodeDefImpl ndi, NodeTypeDef ntd) throws ParseException { |
| while (currentTokenEquals(Lexer.ATTRIBUTE)) { |
| if (currentTokenEquals(Lexer.PRIMARY)) { |
| if (ntd.getPrimaryItemName() != null) { |
| String name = null; |
| try { |
| name = nsMapping.getJCRName(ntd.getName()); |
| } catch (NoPrefixDeclaredException e) { |
| // Should never happen, checked earlier |
| } |
| lexer.fail("More than one primary item specified in node type '" + name + "'"); |
| } |
| ntd.setPrimaryItemName(ndi.getName()); |
| } else if (currentTokenEquals(Lexer.AUTOCREATED)) { |
| ndi.setAutoCreated(true); |
| } else if (currentTokenEquals(Lexer.MANDATORY)) { |
| ndi.setMandatory(true); |
| } else if (currentTokenEquals(Lexer.PROTECTED)) { |
| ndi.setProtected(true); |
| } else if (currentTokenEquals(Lexer.MULTIPLE)) { |
| ndi.setAllowsSameNameSiblings(true); |
| } else if (currentTokenEquals(Lexer.COPY)) { |
| ndi.setOnParentVersion(OnParentVersionAction.COPY); |
| } else if (currentTokenEquals(Lexer.VERSION)) { |
| ndi.setOnParentVersion(OnParentVersionAction.VERSION); |
| } else if (currentTokenEquals(Lexer.INITIALIZE)) { |
| ndi.setOnParentVersion(OnParentVersionAction.INITIALIZE); |
| } else if (currentTokenEquals(Lexer.COMPUTE)) { |
| ndi.setOnParentVersion(OnParentVersionAction.COMPUTE); |
| } else if (currentTokenEquals(Lexer.IGNORE)) { |
| ndi.setOnParentVersion(OnParentVersionAction.IGNORE); |
| } else if (currentTokenEquals(Lexer.ABORT)) { |
| ndi.setOnParentVersion(OnParentVersionAction.ABORT); |
| } |
| nextToken(); |
| } |
| } |
| |
| /** |
| * Converts the given string into a qualified name using the current |
| * namespace mapping. |
| * |
| * @param stringName |
| * @return the qualified name |
| * @throws ParseException if the conversion fails |
| */ |
| private QName toQName(String stringName) throws ParseException { |
| try { |
| return QName.fromJCRName(stringName, nsMapping); |
| } catch (IllegalNameException e) { |
| lexer.fail("Error while parsing '" + stringName + "'", e); |
| return null; |
| } catch (UnknownPrefixException e) { |
| lexer.fail("Error while parsing '" + stringName + "'", e); |
| return null; |
| } |
| } |
| |
| /** |
| * Gets the next token from the underlying lexer. |
| * |
| * @see Lexer#getNextToken() |
| * @throws ParseException if the lexer fails to get the next token. |
| */ |
| private void nextToken() throws ParseException { |
| currentToken = lexer.getNextToken(); |
| } |
| |
| /** |
| * Checks if the {@link #currentToken} is semantically equal to the given |
| * argument. |
| * |
| * @param s the tokens to compare with |
| * @return <code>true</code> if equals; <code>false</code> otherwise. |
| */ |
| private boolean currentTokenEquals(String[] s) { |
| for (int i = 0; i < s.length; i++) { |
| if (currentToken.equals(s[i])) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Checks if the {@link #currentToken} is semantically equal to the given |
| * argument. |
| * |
| * @param c the tokens to compare with |
| * @return <code>true</code> if equals; <code>false</code> 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 <code>true</code> if equals; <code>false</code> otherwise. |
| */ |
| private boolean currentTokenEquals(String s) { |
| return currentToken.equals(s); |
| } |
| |
| } |