001: /*
002: The contents of this file are subject to the Common Public Attribution License
003: Version 1.0 (the "License"); you may not use this file except in compliance with
004: the License. You may obtain a copy of the License at
005: http://www.projity.com/license . The License is based on the Mozilla Public
006: License Version 1.1 but Sections 14 and 15 have been added to cover use of
007: software over a computer network and provide for limited attribution for the
008: Original Developer. In addition, Exhibit A has been modified to be consistent
009: with Exhibit B.
010:
011: Software distributed under the License is distributed on an "AS IS" basis,
012: WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
013: specific language governing rights and limitations under the License. The
014: Original Code is OpenProj. The Original Developer is the Initial Developer and
015: is Projity, Inc. All portions of the code written by Projity are Copyright (c)
016: 2006, 2007. All Rights Reserved. Contributors Projity, Inc.
017:
018: Alternatively, the contents of this file may be used under the terms of the
019: Projity End-User License Agreeement (the Projity License), in which case the
020: provisions of the Projity License are applicable instead of those above. If you
021: wish to allow use of your version of this file only under the terms of the
022: Projity License and not to allow others to use your version of this file under
023: the CPAL, indicate your decision by deleting the provisions above and replace
024: them with the notice and other provisions required by the Projity License. If
025: you do not delete the provisions above, a recipient may use your version of this
026: file under either the CPAL or the Projity License.
027:
028: [NOTE: The text of this license may differ slightly from the text of the notices
029: in Exhibits A and B of the license at http://www.projity.com/license. You should
030: use the latest text at http://www.projity.com/license for your modifications.
031: You may not remove this license text from the source files.]
032:
033: Attribution Information: Attribution Copyright Notice: Copyright © 2006, 2007
034: Projity, Inc. Attribution Phrase (not exceeding 10 words): Powered by OpenProj,
035: an open source solution from Projity. Attribution URL: http://www.projity.com
036: Graphic Image as provided in the Covered Code as file: openproj_logo.png with
037: alternatives listed on http://www.projity.com/logo
038:
039: Display of Attribution Information is required in Larger Works which are defined
040: in the CPAL as a work which combines Covered Code or portions thereof with code
041: not governed by the terms of the CPAL. However, in addition to the other notice
042: obligations, all copies of the Covered Code in Executable and Source Code form
043: distributed must, as a form of attribution of the original author, include on
044: each user interface screen the "OpenProj" logo visible to all users. The
045: OpenProj logo should be located horizontally aligned with the menu bar and left
046: justified on the top left of the screen adjacent to the File menu. The logo
047: must be at least 100 x 25 pixels. When users click on the "OpenProj" logo it
048: must direct them back to http://www.projity.com.
049: */
050: package com.projity.grouping.core;
051:
052: import java.util.ArrayList;
053: /**
054: *
055: */
056:
057: import java.util.Iterator;
058: import java.util.regex.Matcher;
059: import java.util.regex.Pattern;
060: import java.text.DecimalFormat;
061: import java.text.FieldPosition;
062: import java.text.Format;
063: import java.text.ParseException;
064: import java.text.ParsePosition;
065:
066: import org.apache.commons.lang.StringUtils;
067:
068: /**
069: * Class used in converting outline codes to hierarchy
070: */
071: public class OutlineCode extends Format {
072: static final int NUMBERS = 0;
073: static final int UPPERCASE_LETTERS = 1;
074: static final int LOWERCASE_LETTERS = 2;
075: static final int CHARACTERS = 3;
076: private static final int ANY_LENGTH = 0;
077:
078: private ArrayList masks = new ArrayList();
079: private transient Pattern pattern = null;
080:
081: /* (non-Javadoc)
082: * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
083: */
084: public Object parseObject(String code, ParsePosition pos) {
085: Object result = null;
086: Iterator i = masks.iterator();
087: String current = code.substring(pos.getIndex());
088: Matcher matcher = pattern.matcher(current);
089: if (matcher.matches()) {
090: pos.setIndex(pos.getIndex() + matcher.end());
091: return current;
092: } else
093: return null;
094: }
095:
096: public StringBuffer format(Object arg0, StringBuffer arg1,
097: FieldPosition arg2) {
098:
099: // TODO Auto-generated method stub
100: return null;
101: }
102:
103: public boolean isValid(String code) {
104: try {
105: parseObject(code);
106: } catch (ParseException e) {
107: return false;
108: }
109: return true;
110: }
111:
112: public void addMask(Mask mask) {
113: masks.add(mask);
114: rebuildPattern();
115: }
116:
117: private void rebuildPattern() {
118: Iterator i = masks.iterator();
119: pattern = Pattern.compile(getPattern(i, ""));
120: }
121:
122: /**
123: * Recursively build the pattern. Any given sublevel is made optional for its parent
124: * @param i
125: * @return
126: */
127: private String getPattern(Iterator i, String previousSeparator) {
128: StringBuffer pattern = new StringBuffer();
129: Mask mask = (Mask) i.next();
130: pattern.append(mask.getPattern(previousSeparator));
131: if (i.hasNext()) {
132: pattern.append("(?:"
133: + getPattern(i, mask.getSeparatorRegex()) + ")?"); // add next level as optional
134: }
135: return pattern.toString();
136:
137: }
138:
139: public static void main(String[] args) {
140: OutlineCode code = new OutlineCode();
141: code.addMask(new Mask(NUMBERS, ANY_LENGTH, "."));
142: code.addMask(new Mask(UPPERCASE_LETTERS, 2, "."));
143: code.addMask(new Mask(LOWERCASE_LETTERS, ANY_LENGTH, "."));
144:
145: boolean res;
146: res = code.isValid("12.AA.a");
147: res = code.isValid("22212.AA.absdf");
148: res = code.isValid("1");
149: res = code.isValid("12.AA");
150: res = code.isValid("12.");
151: res = code.isValid(".AA");
152: res = code.isValid("A2");
153: res = code.isValid("132");
154: res = code.isValid("12.11");
155: res = code.isValid("12.AA.");
156: }
157:
158: public static class Mask {
159: static final int NUMBERS = 0;
160: static final int UPPERCASE_LETTERS = 1;
161: static final int LOWERCASE_LETTERS = 2;
162: static final int CHARACTERS = 3;
163: private static final int ANY_LENGTH = 0;
164:
165: int type = NUMBERS;
166: int length; //The maximum length in characters of the outline code values, from 1-255. If length is any, the value is zero.
167: String separator; // must be non-alphanumeric
168:
169: /**
170: * Gets a regular expression for this mask
171: * @param previousSeparator - if not empty, this expression is prefixed with previous mask's separator
172: * @return
173: */
174: StringBuffer getPattern(String previousSeparator) {
175: StringBuffer result = new StringBuffer("(");
176: result.append(previousSeparator);
177: switch (type) {
178: case NUMBERS:
179: result.append("\\d");
180: break;
181: case UPPERCASE_LETTERS:
182: result.append("[A-Z]");
183: break;
184: case LOWERCASE_LETTERS:
185: result.append("[a-z]");
186: break;
187: case CHARACTERS:
188: result.append("[^" + separator + "]");
189: break;
190: }
191: if (length == ANY_LENGTH)
192: result.append("+"); // at least one
193: else
194: result.append("{" + length + "}"); // exactly <length> times
195:
196: result.append(")");
197: return result;
198: }
199:
200: String nextValue(String current) {
201: String result = current; //TODO is this needed?
202: switch (type) {
203: case NUMBERS:
204: int value = Integer.parseInt(current) + 1;
205: if (length == ANY_LENGTH)
206: result = "" + value;
207: else
208: result = new DecimalFormat(StringUtils.repeat("0",
209: length))
210: .format(Integer.getInteger(current));
211: break;
212: }
213: return result;
214: }
215:
216: public Mask() {
217: }
218:
219: public Mask(int type, int length, String separator) {
220: this .type = type;
221: this .length = length;
222: this .separator = separator;
223: }
224:
225: /**
226: * @return Returns the length.
227: */
228: public int getLength() {
229: return length;
230: }
231:
232: /**
233: * @param length The length to set.
234: */
235: public void setLength(int length) {
236: this .length = length;
237: }
238:
239: /**
240: * @return Returns the separator.
241: */
242: public String getSeparator() {
243: return separator;
244: }
245:
246: public String getSeparatorRegex() {
247: return "\\" + separator; // it is allowed to escape any nonalpha character
248: }
249:
250: /**
251: * @param separator The separator to set.
252: */
253: public void setSeparator(String separator) {
254: this .separator = separator;
255: }
256:
257: /**
258: * @return Returns the type.
259: */
260: public int getType() {
261: return type;
262: }
263:
264: /**
265: * @param type The type to set.
266: */
267: public void setType(int type) {
268: this.type = type;
269: }
270: }
271:
272: }
|