001: package org.tigris.scarab.util;
002:
003: /* ================================================================
004: * Copyright (c) 2000-2002 CollabNet. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are
008: * met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowlegement: "This product includes
019: * software developed by Collab.Net <http://www.Collab.Net/>."
020: * Alternately, this acknowlegement may appear in the software itself, if
021: * and wherever such third-party acknowlegements normally appear.
022: *
023: * 4. The hosted project names must not be used to endorse or promote
024: * products derived from this software without prior written
025: * permission. For written permission, please contact info@collab.net.
026: *
027: * 5. Products derived from this software may not use the "Tigris" or
028: * "Scarab" names nor may "Tigris" or "Scarab" appear in their names without
029: * prior written permission of Collab.Net.
030: *
031: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
032: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
033: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
034: * IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
035: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
036: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
037: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
038: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
039: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
040: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
041: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
042: *
043: * ====================================================================
044: *
045: * This software consists of voluntary contributions made by many
046: * individuals on behalf of Collab.Net.
047: */
048:
049: // JDK classes
050: import java.util.List;
051: import java.util.Iterator;
052: import java.util.ArrayList;
053: import java.util.Map;
054: import java.util.HashMap;
055:
056: import org.apache.regexp.RECompiler;
057: import org.apache.regexp.REProgram;
058: import org.apache.regexp.RE;
059:
060: // Turbine classes
061: import org.apache.torque.TorqueException;
062:
063: // Scarab classes
064: import org.tigris.scarab.om.Module;
065: import org.tigris.scarab.om.Issue;
066: import org.tigris.scarab.om.IssueManager;
067:
068: /**
069: * This class contains logic for finding issue ids in generic text.
070: *
071: * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
072: * @version $Id: IssueIdParser.java 7604 2003-04-09 22:55:31Z jmcnally $
073: */
074: public class IssueIdParser {
075: private static REProgram idREProgram = null;
076:
077: static {
078: try {
079: RECompiler rec = new RECompiler();
080: idREProgram = rec.compile("[:alpha:]*\\d+");
081: } catch (Exception e) {
082: Log.get().error(
083: "An npe is going to occur because I could not "
084: + "compile regex: [:alpha:]*\\d+", e);
085: }
086: }
087:
088: /**
089: * Parses text for any valid issue ids and returns matches.
090: * The regular expression to determine ids is given by the module
091: */
092: public static List getIssueIdTokens(Module module, String text)
093: throws TorqueException {
094: List result = new ArrayList();
095: RE re = new RE(module.getIssueRegex());
096: re.setMatchFlags(RE.MATCH_CASEINDEPENDENT);
097: int pos = 0;
098: while (re.match(text, pos)) {
099: Log.get()
100: .debug(
101: re.getParen(0) + " found at "
102: + re.getParenStart(0));
103: result.add(re.getParen(0));
104: pos = re.getParenEnd(0);
105: }
106:
107: return result;
108: }
109:
110: /**
111: * Parses text for any valid issue ids. The text is broken up
112: * into tokens at potential id boundaries. if a token corresponds
113: * to a valid issue id, a List is returned with [0] as the
114: * token and [1] is the id. if a token does not contain an id
115: * the text is added as a String.
116: */
117: public static List tokenizeText(Module module, String text)
118: throws TorqueException {
119: List result = new ArrayList();
120: RE re = new RE(module.getIssueRegex());
121: re.setMatchFlags(RE.MATCH_CASEINDEPENDENT);
122: int pos = 0;
123: while (re.match(text, pos)) {
124: Log.get()
125: .debug(
126: re.getParen(0) + " found at "
127: + re.getParenStart(0));
128: // Add any text that did not contain an id
129: if (re.getParenStart(0) > pos) {
130: result.add(text.substring(pos, re.getParenStart(0)));
131: }
132:
133: String token = re.getParen(0);
134: String id = getIssueIdFromToken(module, token);
135: if (id == null) {
136: result.add(token);
137: } else {
138: List tokenId = new ArrayList(2);
139: tokenId.add(token);
140: tokenId.add(id);
141: result.add(tokenId);
142: }
143:
144: pos = re.getParenEnd(0);
145: }
146:
147: if (pos < text.length()) {
148: result.add(text.substring(pos));
149: }
150:
151: return result;
152: }
153:
154: /**
155: * Assumption is the token will contain at most one issue id.
156: * A number is a potential valid id within the given module.
157: * A syntactically correct id will be checked against the db
158: * to make sure a valid issue exists. if the token does not
159: * result in a valid issue, null is returned. Otherwise the
160: * id will be returned including the module code.
161: *
162: * @param module a <code>Module</code> value
163: * @param token a <code>String</code> value
164: * @return a <code>String</code> value
165: */
166: public static String getIssueIdFromToken(Module module, String token) {
167: RE re = new RE(idREProgram);
168: re.setMatchFlags(RE.MATCH_CASEINDEPENDENT);
169: String id = null;
170: if (re.match(token)) {
171: id = re.getParen(0);
172: Issue issue = null;
173: try {
174: // is the issue required to be in the module? the current
175: // code will return an issue with Code that is the same
176: // as the given module.
177: issue = IssueManager.getIssueById(id, module.getCode());
178: } catch (Exception e) {
179: // Ignored on purpose
180: }
181: if (issue == null || issue.getDeleted()) {
182: id = null;
183: }
184: }
185: return id;
186: }
187:
188: /**
189: * A Map of ids where the keys are the tokens such as "issue#35" and the
190: * value is the unique id, "PACS35".
191: */
192: public static Map getIssueIds(Module module, String text)
193: throws TorqueException {
194: List tokens = getIssueIdTokens(module, text);
195: Map idMap = new HashMap(presizeMap(tokens.size()));
196: Iterator i = tokens.iterator();
197: while (i.hasNext()) {
198: String token = (String) i.next();
199: String id = getIssueIdFromToken(module, token);
200: if (id != null) {
201: idMap.put(token, id);
202: }
203: }
204: return idMap;
205: }
206:
207: private static int presizeMap(int size) {
208: return (int) (1.25 * size + 1.0);
209: }
210: }
|