001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.pde.internal.core.util;
011:
012: import java.util.HashMap;
013: import java.util.HashSet;
014: import java.util.Iterator;
015: import java.util.regex.Matcher;
016: import java.util.regex.Pattern;
017:
018: /**
019: * PDETextHelper
020: *
021: */
022: public class PDETextHelper {
023:
024: public static final String F_DOTS = "..."; //$NON-NLS-1$
025:
026: /**
027: * @param text
028: * @return
029: */
030: public static String truncateAndTrailOffText(String text, int limit) {
031: String trimmed = text.trim();
032: int dotsLength = F_DOTS.length();
033: int trimmedLength = trimmed.length();
034: int limitWithDots = limit - dotsLength;
035:
036: if (limit >= trimmedLength) {
037: return trimmed;
038: }
039: // limit <= trimmedLength
040: if (limit <= dotsLength) {
041: return ""; //$NON-NLS-1$
042: }
043: // dotsLength < limit < trimmedLength
044: return trimmed.substring(0, limitWithDots) + F_DOTS;
045: }
046:
047: /**
048: * @param text
049: * @return
050: */
051: public static boolean isDefined(String text) {
052: if ((text == null) || (text.length() == 0)) {
053: return false;
054: }
055: return true;
056: }
057:
058: /**
059: * @param text
060: * @return
061: */
062: public static boolean isDefinedAfterTrim(String text) {
063: if (text == null) {
064: return false;
065: }
066: String trimmedText = text.trim();
067: if (trimmedText.length() == 0) {
068: return false;
069: }
070: return true;
071: }
072:
073: /**
074: * Strips \n, \r, \t
075: * Strips leading spaces, trailing spaces, duplicate spaces
076: * @param text
077: * @return never null
078: */
079: public static String translateReadText(String text) {
080: // Ensure not null
081: if (text == null) {
082: return ""; //$NON-NLS-1$
083: }
084: String result = ""; //$NON-NLS-1$
085: // Get rid of leading and trailing whitespace
086: String inputText = text.trim();
087: int length = inputText.length();
088: char previousChar = ' ';
089: StringBuffer buffer = new StringBuffer(length);
090: // Visit each character in text
091: for (int i = 0; i < length; i++) {
092: char currentChar = inputText.charAt(i);
093:
094: if ((currentChar == '\r') || (currentChar == '\n')
095: || (currentChar == '\t')) {
096: // Convert newlines, carriage returns and tabs to spaces
097: currentChar = ' ';
098: }
099:
100: if (currentChar == ' ') {
101: // Skip multiple spaces
102: if (previousChar != ' ') {
103: buffer.append(currentChar);
104: previousChar = currentChar;
105: }
106: } else {
107: buffer.append(currentChar);
108: previousChar = currentChar;
109: }
110: }
111: result = buffer.toString();
112: if (PDEHTMLHelper.isAllWhitespace(result)) {
113: return ""; //$NON-NLS-1$
114: }
115: return result;
116: }
117:
118: /**
119: * @param text
120: * @param substituteChars
121: * @return
122: */
123: public static String translateWriteText(String text,
124: HashMap substituteChars) {
125: return translateWriteText(text, null, substituteChars);
126: }
127:
128: /**
129: * @param text
130: * @param tagExceptions
131: * @param substituteChars
132: * @return
133: */
134: public static String translateWriteText(String text,
135: HashSet tagExceptions, HashMap substituteChars) {
136: // Ensure not null
137: if (text == null) {
138: return ""; //$NON-NLS-1$
139: }
140: // Process tag exceptions if provided
141: boolean processTagExceptions = false;
142: int scanLimit = 0;
143: if ((tagExceptions != null)
144: && (tagExceptions.isEmpty() == false)) {
145: processTagExceptions = true;
146: // Use the biggest entry in the set as the limit
147: scanLimit = determineMaxLength(tagExceptions);
148: }
149: // Process substitute characters if provided
150: boolean processSubstituteChars = false;
151: if ((substituteChars != null)
152: && (substituteChars.isEmpty() == false)) {
153: processSubstituteChars = true;
154: }
155: // Translated buffer
156: StringBuffer buffer = new StringBuffer(text.length());
157: // Visit each character in text
158: for (IntegerPointer index = new IntegerPointer(0); index
159: .getInteger() < text.length(); index.increment()) {
160: // Process the current character
161: char currentChar = text.charAt(index.getInteger());
162: boolean processed = false;
163: // If we are processing tag exceptions, check to see if this
164: // character is part of a tag exception and process it accordingly
165: // if it is
166: if ((processed == false) && (processTagExceptions == true)) {
167: processed = processTagExceptions(currentChar,
168: substituteChars, tagExceptions, buffer,
169: scanLimit, text, index);
170: }
171: // If the character was not part of a tag exception and we are
172: // processing substitution characters, check to see if this
173: // character needs to be translated and process it accordingly if
174: // it is
175: if ((processed == false)
176: && (processSubstituteChars == true)) {
177: processed = processSubstituteChars(currentChar,
178: substituteChars, buffer);
179: }
180: // If the character did not need to be translated, just append it
181: // as is to the buffer
182: if (processed == false) {
183: buffer.append(currentChar);
184: }
185: }
186: // Return the translated buffer
187: return buffer.toString();
188: }
189:
190: /**
191: * @param currentChar
192: * @param substituteChars
193: * @param buffer
194: * @return
195: */
196: private static boolean processSubstituteChars(char currentChar,
197: HashMap substituteChars, StringBuffer buffer) {
198: Character character = new Character(currentChar);
199: if (substituteChars.containsKey(character)) {
200: String value = (String) substituteChars.get(character);
201: if (isDefined(value)) {
202: // Append the value if defined
203: buffer.append(value);
204: }
205: // If the value was not defined, then we will strip the character
206: return true;
207: }
208: return false;
209: }
210:
211: /**
212: * @param currentChar
213: * @param tagExceptions
214: * @param buffer
215: * @param scanLimit
216: * @param inputText
217: * @param index
218: * @return
219: */
220: private static boolean processTagExceptions(char currentChar,
221: HashMap substituteChars, HashSet tagExceptions,
222: StringBuffer buffer, int scanLimit, String text,
223: IntegerPointer index) {
224: // If the current character is an open angle bracket, then it may be
225: // part of a valid tag exception
226: if (currentChar == '<') {
227: // Determine whether this bracket is part of a tag that is a
228: // valid tag exception
229: // Respect character array boundaries. Adjust accordingly
230: int limit = text.length();
231: // Scan ahead buffer
232: StringBuffer parsedText = new StringBuffer();
233: // Scan ahead in text to parse out a possible element tag name
234: for (int j = index.getInteger() + 1; j < limit; j++) {
235: char futureChar = text.charAt(j);
236: if (futureChar == '>') {
237: // An ending bracket was found
238: // This is indeed a element tag
239: // Determine if the element tag we found is a valid
240: // tag exception
241: String futureBuffer = parsedText.toString();
242: if (isValidTagException(tagExceptions, futureBuffer)) {
243: // The element tag is a valid tag exception
244: // Process the tag exception characters
245: processTagExceptionCharacters(substituteChars,
246: buffer, futureBuffer);
247: // Fast forward the current index to the scanned ahead
248: // index to skip what we just found
249: index.setInteger(j);
250: return true;
251: }
252: return false;
253: }
254: // Accumulate the possible element tag name
255: parsedText.append(futureChar);
256: }
257: }
258: return false;
259: }
260:
261: /**
262: * @param substituteChars
263: * @param buffer
264: * @param text
265: */
266: private static void processTagExceptionCharacters(
267: HashMap substituteChars, StringBuffer buffer, String text) {
268: // Get the tag name
269: String tagName = getTagName(text);
270: // Determine if there is a trailing forward slash
271: boolean trailingSlash = text.endsWith("/"); //$NON-NLS-1$
272: // Extract the attribute list of the element tag content
273: // It may contain trailing spaces or a trailing '/'
274: String attributeList = text.substring(tagName.length());
275: // If the attribute list is not valid, discard the attribute list
276: if ((isValidTagAttributeList(attributeList) == false)) {
277: buffer.append('<');
278: buffer.append(tagName);
279: // Since, this tag has an attribute list and we are discarding it,
280: // we have to make sure to replace the trailing slash if it had one
281: if (trailingSlash) {
282: buffer.append('/');
283: }
284: buffer.append('>');
285: return;
286: } else if (attributeList.length() == 0) {
287: // If the tag has no attribute list then just return the tag
288: // as is (trailing slash is already including in the tag name)
289: buffer.append('<');
290: buffer.append(tagName);
291: buffer.append('>');
292: return;
293: }
294: boolean inQuote = false;
295: // Append the opening element bracket
296: buffer.append('<');
297: // Traverse the tag element content character by character
298: // Translate any substitution characters required only inside attribute
299: // value double-quotes
300: for (int i = 0; i < text.length(); i++) {
301: boolean processed = false;
302: char currentChar = text.charAt(i);
303: boolean onQuote = (currentChar == '"');
304: // Determine whether we are currently processing the quote character
305: if (onQuote) {
306: if (inQuote) {
307: // Quote encountered is an end quote
308: inQuote = false;
309: } else {
310: // Quote encountered is a begin quote
311: inQuote = true;
312: }
313: }
314: // If we are currently within an attribute value double-quotes and
315: // not on a quote character, translate this character if necessary
316: if (inQuote && !onQuote) {
317: processed = processSubstituteChars(currentChar,
318: substituteChars, buffer);
319: }
320: // If the character did not require translation, just append it
321: // as-is
322: if (processed == false) {
323: buffer.append(currentChar);
324: }
325: }
326: // Append the closing element bracket
327: buffer.append('>');
328: }
329:
330: /**
331: * @param tagExceptions
332: * @param buffer
333: * @return
334: */
335: private static boolean isValidTagException(HashSet tagExceptions,
336: String buffer) {
337: // Sample buffer format:
338: // NO '<'
339: // tagName att1="value" att2="value"
340: // NO '>'
341: // Parse tag name and ignore attributes (if any specified)
342: String tagName = getTagName(buffer);
343: // Check to see if the tag name is a tag exception
344: if (tagExceptions.contains(tagName)) {
345: return true;
346: }
347: return false;
348: }
349:
350: /**
351: * @param text
352: * @return
353: */
354: private static boolean isValidTagAttributeList(String text) {
355: // Determine whether the given attribute list is formatted in the
356: // valid name="value" XML format
357: // Sample formats:
358: // " att1="value1" att2="value2"
359: // " att1="value1" att2="value2 /"
360: // " att1="value1"
361:
362: // space attributeName space = space "attributeValue" space /
363: String patternString = "^([\\s]+[A-Za-z0-9_:\\-\\.]+[\\s]?=[\\s]?\".+?\")*[\\s]*[/]?$"; //$NON-NLS-1$
364: Pattern pattern = Pattern.compile(patternString);
365: Matcher matcher = pattern.matcher(text);
366: // Determine whether the given attribute list matches the pattern
367: return matcher.find();
368: }
369:
370: /**
371: * @param buffer
372: * @return
373: */
374: private static String getTagName(String buffer) {
375: // Sample buffer format:
376: // NO '<'
377: // tagName att1="value" att2="value"
378: // NO '>'
379: StringBuffer tagName = new StringBuffer();
380: // The tag name is every non-whitespace character in the buffer until
381: // a whitespace character is encountered
382: for (int i = 0; i < buffer.length(); i++) {
383: char character = buffer.charAt(i);
384: if (Character.isWhitespace(character)) {
385: break;
386: }
387: tagName.append(character);
388: }
389: return tagName.toString();
390: }
391:
392: /**
393: * @param set
394: * @return
395: */
396: private static int determineMaxLength(HashSet set) {
397: Iterator iterator = set.iterator();
398: int maxLength = -1;
399: while (iterator.hasNext()) {
400: // Has to be a String
401: String object = (String) iterator.next();
402: if (object.length() > maxLength) {
403: maxLength = object.length();
404: }
405: }
406: return maxLength;
407: }
408:
409: /**
410: * IntegerPointer
411: *
412: */
413: private static class IntegerPointer {
414:
415: private int fInteger;
416:
417: /**
418: *
419: */
420: public IntegerPointer(int integer) {
421: fInteger = integer;
422: }
423:
424: /**
425: * @return
426: */
427: public int getInteger() {
428: return fInteger;
429: }
430:
431: /**
432: * @param integer
433: */
434: public void setInteger(int integer) {
435: fInteger = integer;
436: }
437:
438: /**
439: *
440: */
441: public void increment() {
442: fInteger++;
443: }
444: }
445:
446: }
|