001: /*
002: * Token.java
003: *
004: * Copyright (C) 2007 Ferran Busquets
005: *
006: * This program is free software: you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation, either version 3 of the License, or
009: * any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program. If not, see <http://www.gnu.org/licenses/>.
018: *
019: */
020: package org.naturalcli;
021:
022: /**
023: * <p>
024: * The <code>Token</code> class implements a token for the
025: * command grammar.
026: * </p>
027: *
028: * @author Ferran Busquets
029: */
030: public class Token {
031:
032: /** Beginning char for a parameter */
033: static private final char CHAR_BEGIN_PARAM = '<';
034:
035: /** Ending char for a parameter */
036: static private final char CHAR_END_PARAM = '>';
037:
038: /** Beginning char for an optional token*/
039: static private final char CHAR_BEGIN_OPT = '[';
040:
041: /** Ending char for an optional token */
042: static private final char CHAR_END_OPT = ']';
043:
044: /** Char separator for a parameter name and type */
045: static private final char CHAR_NAME_TYPE = ':';
046:
047: /** String for variable arguments token */
048: static private final String VAR_ARGS = "...";
049:
050: /** Texts giving sense to the token */
051: private String text;
052:
053: /**
054: * Constructor for the token
055: *
056: * @param text the token text
057: * @throws InvalidTokenException
058: * @throws InvalidTokenException
059: */
060: public Token(String text) throws InvalidTokenException {
061: this .setText(text);
062: }
063:
064: /**
065: * Get the token text
066: *
067: * @return the token text
068: */
069: public String getText() {
070: return text;
071: }
072:
073: /**
074: * Set the token text and validate it
075: *
076: * @param text the token text to set
077: * @throws InvalidTokenException
078: * @throws InvalidTokenException
079: */
080: public void setText(String text) throws InvalidTokenException {
081: validate(text);
082: this .text = text;
083: }
084:
085: private void validate(String text) throws InvalidTokenException {
086: // Validate null
087: if (text == null)
088: throw new InvalidTokenException("Null token text");
089: if (text.length() == 0)
090: throw new InvalidTokenException("Empty token text");
091: // Validate invalid optional parameters
092: String bad_start = new String(new char[] { CHAR_BEGIN_PARAM,
093: CHAR_BEGIN_OPT });
094: String bad_end = new String(new char[] { CHAR_END_OPT,
095: CHAR_END_PARAM });
096: String opt_par_start = new String(new char[] { CHAR_BEGIN_OPT,
097: CHAR_BEGIN_PARAM });
098: String opt_par_end = new String(new char[] { CHAR_END_PARAM,
099: CHAR_END_OPT, });
100: if (text.startsWith(bad_start) || text.endsWith(bad_end))
101: throw new InvalidTokenException(
102: "Bad optional parameter token");
103: if (text.startsWith(opt_par_start)
104: && !text.endsWith(opt_par_end))
105: throw new InvalidTokenException(
106: "Bad optional parameter token");
107: if (!text.startsWith(opt_par_start)
108: && text.endsWith(opt_par_end))
109: throw new InvalidTokenException(
110: "Bad optional parameter token");
111: // Validate the options format
112: char first = text.charAt(0);
113: char last = text.charAt(text.length() - 1);
114: if (first == CHAR_BEGIN_OPT && last != CHAR_END_OPT)
115: throw new InvalidTokenException("Bad optional token");
116: if (first != CHAR_BEGIN_OPT && last == CHAR_END_OPT)
117: throw new InvalidTokenException("Bad optional token");
118: // Validate the parameter format
119: if (first == CHAR_BEGIN_PARAM && last != CHAR_END_PARAM)
120: throw new InvalidTokenException("Bad parameter token");
121: if (first != CHAR_BEGIN_PARAM && last == CHAR_END_PARAM)
122: throw new InvalidTokenException("Bad parameter token");
123: }
124:
125: /**
126: * Checks if it's an optional token
127: *
128: * @return <code>true</code> if its optional, <code>false</code> otherwise
129: */
130: public boolean isOptional() {
131: boolean begin = text.charAt(0) == CHAR_BEGIN_OPT;
132: boolean end = text.charAt(text.length() - 1) == CHAR_END_OPT;
133: return begin && end;
134: }
135:
136: /**
137: * Checks if it's a parameter token
138: *
139: * @return <code>true</code> if it's a parameter, <code>false</code> otherwise
140: */
141: public boolean isParameter() {
142: boolean begin, end;
143: boolean opt = this .isOptional();
144: begin = text.charAt(opt ? 1 : 0) == CHAR_BEGIN_PARAM;
145: end = text.charAt(text.length() - (opt ? 2 : 1)) == CHAR_END_PARAM;
146: return begin && end;
147: }
148:
149: /**
150: * Checks if it's an optional parameter token
151: *
152: * @return <code>true</code> if its optional parameter, <code>false</code> otherwise
153: */
154: public boolean isOptionalParameter() {
155: return isParameter() && isOptional();
156: }
157:
158: /**
159: * Checks if it's a mandatory parameter token
160: *
161: * @return <code>true</code> if it's mandatory parameter, <code>false</code> otherwise
162: */
163: public boolean isMandatoryParameter() {
164: return isParameter() && !isOptional();
165: }
166:
167: /**
168: * Helper method for {@link org.naturalcli.Token#getParameterName()} and
169: * {@link org.naturalcli.Token#getParameterName()}
170: *
171: * @param n1t2 the info requested. 1 for name and 2 for type
172: * @return the parameter name, type name or <code>null</code>
173: */
174: private String getParameterInfo(int n1t2) {
175: if (n1t2 != 1 && n1t2 != 2)
176: throw new RuntimeException("Bad value for n1t2.");
177: if (!this .isParameter())
178: return null;
179: String word = this .getWord();
180: int i = word.indexOf(CHAR_NAME_TYPE);
181: if (i == -1)
182: return word;
183: return (n1t2 == 1) ? word.substring(0, i) : word
184: .substring(i + 1);
185: }
186:
187: /**
188: * Gets the parameter name for the token
189: *
190: * @return the parameter name, or <code>null</code> if it's not a parameter.
191: */
192: public String getParameterName() {
193: return this .getParameterInfo(1);
194: }
195:
196: /**
197: * Gets the parameter type name for the token
198: *
199: * @return the parameter type name, or <code>null</code> if it's not a parameter.
200: */
201: public String getParameterTypeName() {
202: return this .getParameterInfo(2);
203: }
204:
205: /**
206: * Determines if it's an identifier
207: *
208: * @return <code>true</code> if it's an identifier, <code>false</code> otherwise
209: */
210: public boolean isIdentifier() {
211: return !this .isParameter();
212: }
213:
214: /**
215: * Determines if it's a variable arguments token
216: *
217: * @return <code>true</code> if it's a variable arguments token, <code>false</code> otherwise
218: */
219: public boolean isVarArgs() {
220: return this .text.equals(VAR_ARGS);
221: }
222:
223: /**
224: * Obtains the word that represents the token, it means
225: * the token text without optional or parameter chars
226: *
227: * @return the word that represents the token
228: */
229: public String getWord() {
230: String word = new String(text);
231: if (isOptionalParameter())
232: return text.substring(2, text.length() - 2);
233: else if (isOptional() || isParameter())
234: return text.substring(1, text.length() - 1);
235: return word;
236: }
237:
238: /**
239: * Checks the text given to see if it's like the token
240: *
241: * @param text the text to match
242: * @param pv the parameter validator
243: * @return <code>true</code> if matches, <code>false</code> if not
244: * @throws UnknownParameterType
245: * @throws UnknownParameterType
246: */
247: public boolean matches(String t, ParameterValidator pv)
248: throws UnknownParameterType {
249: if (this .isIdentifier())
250: return this .getWord().equals(t);
251: else if (this .isParameter())
252: return pv.validate(t, this .getParameterTypeName()) == null;
253: return false;
254: }
255:
256: }
|