001/* 002 * Copyright (C) 2009-2011 Mathias Doenitz 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.parboiled.matchers; 018 019import org.parboiled.MatcherContext; 020import org.parboiled.Rule; 021import org.parboiled.common.ImmutableList; 022import org.parboiled.common.Utils; 023import org.parboiled.trees.ImmutableGraphNode; 024 025import static org.parboiled.common.Preconditions.checkArgNotNull; 026 027/** 028 * Abstract base class of most regular {@link Matcher}s. 029 */ 030public abstract class AbstractMatcher extends ImmutableGraphNode<Matcher> implements Matcher, Cloneable { 031 private String label; 032 private boolean nodeSuppressed; 033 private boolean subnodesSuppressed; 034 private boolean nodeSkipped; 035 private Object tag; 036 037 public AbstractMatcher(String label) { 038 this(new Rule[0], label); 039 } 040 041 public AbstractMatcher(Rule subRule, String label) { 042 this(new Rule[] {checkArgNotNull(subRule, "subRule")}, label); 043 } 044 045 public AbstractMatcher(Rule[] subRules, String label) { 046 super(ImmutableList.<Matcher>of(toMatchers(checkArgNotNull(subRules, "subRules")))); 047 this.label = label; 048 } 049 050 private static Matcher[] toMatchers(Rule[] subRules) { 051 Matcher[] matchers = new Matcher[subRules.length]; 052 for (int i = 0; i < subRules.length; i++) { 053 matchers[i] = (Matcher) subRules[i]; 054 } 055 return matchers; 056 } 057 058 public boolean isNodeSuppressed() { 059 return nodeSuppressed; 060 } 061 062 public boolean areSubnodesSuppressed() { 063 return subnodesSuppressed; 064 } 065 066 public boolean isNodeSkipped() { 067 return nodeSkipped; 068 } 069 070 public boolean areMismatchesMemoed() { 071 return false; 072 } 073 074 public String getLabel() { 075 return label; 076 } 077 078 public boolean hasCustomLabel() { 079 // this is the default implementation for single character matchers 080 // complex matchers override with a custom implementation 081 return true; 082 } 083 084 @Override 085 public String toString() { 086 return getLabel(); 087 } 088 089 public AbstractMatcher label(String label) { 090 if (Utils.equal(label, this.label)) return this; 091 AbstractMatcher clone = createClone(); 092 clone.label = label; 093 return clone; 094 } 095 096 public Rule suppressNode() { 097 if (nodeSuppressed) return this; 098 AbstractMatcher clone = createClone(); 099 clone.nodeSuppressed = true; 100 return clone; 101 } 102 103 public Rule suppressSubnodes() { 104 if (subnodesSuppressed) return this; 105 AbstractMatcher clone = createClone(); 106 clone.subnodesSuppressed = true; 107 return clone; 108 } 109 110 public Rule skipNode() { 111 if (nodeSkipped) return this; 112 AbstractMatcher clone = createClone(); 113 clone.nodeSkipped = true; 114 return clone; 115 } 116 117 public Rule memoMismatches() { 118 return new MemoMismatchesMatcher(this); 119 } 120 121 public Object getTag() { 122 return tag; 123 } 124 public void setTag(Object tagObject) { 125 tag = tagObject; 126 } 127 128 // default implementation is to simply delegate to the context 129 public MatcherContext getSubContext(MatcherContext context) { 130 return context.getSubContext(this); 131 } 132 133 // creates a shallow copy 134 private AbstractMatcher createClone() { 135 try { 136 return (AbstractMatcher) clone(); 137 } catch (CloneNotSupportedException e) { 138 throw new IllegalStateException(); 139 } 140 } 141 142} 143