001: /*
002: * StandardUtilities.java - Various miscallaneous utility functions
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 1999, 2006 Matthieu Casanova, Slava Pestov
007: * Portions copyright (C) 2000 Richard S. Hall
008: * Portions copyright (C) 2001 Dirk Moebius
009: *
010: * This program is free software; you can redistribute it and/or
011: * modify it under the terms of the GNU General Public License
012: * as published by the Free Software Foundation; either version 2
013: * of the License, or any later version.
014: *
015: * This program is distributed in the hope that it will be useful,
016: * but WITHOUT ANY WARRANTY; without even the implied warranty of
017: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
018: * GNU General Public License for more details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with this program; if not, write to the Free Software
022: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
023: */
024:
025: package org.gjt.sp.util;
026:
027: //{{{ Imports
028: import javax.swing.text.Segment;
029: import java.util.Comparator;
030: import java.util.Stack;
031:
032: //}}}
033:
034: /**
035: * Several tools that depends on JDK only.
036: *
037: * @author Matthieu Casanova
038: * @version $Id: StandardUtilities.java 9871 2007-06-28 16:33:20Z Vampire0 $
039: * @since 4.3pre5
040: */
041: public class StandardUtilities {
042:
043: //{{{ Text methods
044:
045: //{{{ getPrevIndentStyle() method
046: /**
047: * @param str A java string
048: * @return the leading whitespace of that string, for indenting subsequent lines.
049: * @since jEdit 4.3pre10
050: */
051: public static String getIndentString(String str) {
052:
053: StringBuffer indentString = new StringBuffer();
054: int idx = 0;
055: char ch = str.charAt(idx);
056: while (idx < str.length() && Character.isWhitespace(ch)) {
057: indentString.append(ch);
058: idx++;
059: ch = str.charAt(idx);
060: }
061: return indentString.toString();
062:
063: } //}}}
064:
065: //{{{ getLeadingWhiteSpace() method
066: /**
067: * Returns the number of leading white space characters in the
068: * specified string.
069: *
070: * @param str The string
071: */
072: public static int getLeadingWhiteSpace(String str) {
073: int whitespace = 0;
074: loop: for (; whitespace < str.length();) {
075: switch (str.charAt(whitespace)) {
076: case ' ':
077: case '\t':
078: whitespace++;
079: break;
080: default:
081: break loop;
082: }
083: }
084: return whitespace;
085: } //}}}
086:
087: //{{{ getTrailingWhiteSpace() method
088: /**
089: * Returns the number of trailing whitespace characters in the
090: * specified string.
091: * @param str The string
092: */
093: public static int getTrailingWhiteSpace(String str) {
094: int whitespace = 0;
095: loop: for (int i = str.length() - 1; i >= 0; i--) {
096: switch (str.charAt(i)) {
097: case ' ':
098: case '\t':
099: whitespace++;
100: break;
101: default:
102: break loop;
103: }
104: }
105: return whitespace;
106: } //}}}
107:
108: //{{{ getLeadingWhiteSpaceWidth() method
109: /**
110: * Returns the width of the leading white space in the specified
111: * string.
112: * @param str The string
113: * @param tabSize The tab size
114: */
115: public static int getLeadingWhiteSpaceWidth(String str, int tabSize) {
116: int whitespace = 0;
117: loop: for (int i = 0; i < str.length(); i++) {
118: switch (str.charAt(i)) {
119: case ' ':
120: whitespace++;
121: break;
122: case '\t':
123: whitespace += tabSize - whitespace % tabSize;
124: break;
125: default:
126: break loop;
127: }
128: }
129: return whitespace;
130: } //}}}
131:
132: //{{{ createWhiteSpace() method
133: /**
134: * Creates a string of white space with the specified length.<p>
135: *
136: * To get a whitespace string tuned to the current buffer's
137: * settings, call this method as follows:
138: *
139: * <pre>myWhitespace = MiscUtilities.createWhiteSpace(myLength,
140: * (buffer.getBooleanProperty("noTabs") ? 0
141: * : buffer.getTabSize()));</pre>
142: *
143: * @param len The length
144: * @param tabSize The tab size, or 0 if tabs are not to be used
145: */
146: public static String createWhiteSpace(int len, int tabSize) {
147: return createWhiteSpace(len, tabSize, 0);
148: } //}}}
149:
150: //{{{ createWhiteSpace() method
151: /**
152: * Creates a string of white space with the specified length.<p>
153: *
154: * To get a whitespace string tuned to the current buffer's
155: * settings, call this method as follows:
156: *
157: * <pre>myWhitespace = MiscUtilities.createWhiteSpace(myLength,
158: * (buffer.getBooleanProperty("noTabs") ? 0
159: * : buffer.getTabSize()));</pre>
160: *
161: * @param len The length
162: * @param tabSize The tab size, or 0 if tabs are not to be used
163: * @param start The start offset, for tab alignment
164: */
165: public static String createWhiteSpace(int len, int tabSize,
166: int start) {
167: StringBuilder buf = new StringBuilder();
168: if (tabSize == 0) {
169: while (len-- > 0)
170: buf.append(' ');
171: } else if (len == 1)
172: buf.append(' ');
173: else {
174: int count = (len + start % tabSize) / tabSize;
175: if (count != 0)
176: len += start;
177: while (count-- > 0)
178: buf.append('\t');
179: count = len % tabSize;
180: while (count-- > 0)
181: buf.append(' ');
182: }
183: return buf.toString();
184: } //}}}
185:
186: //{{{ getVirtualWidth() method
187: /**
188: * Returns the virtual column number (taking tabs into account) of the
189: * specified offset in the segment.
190: *
191: * @param seg The segment
192: * @param tabSize The tab size
193: */
194: public static int getVirtualWidth(Segment seg, int tabSize) {
195: int virtualPosition = 0;
196:
197: for (int i = 0; i < seg.count; i++) {
198: char ch = seg.array[seg.offset + i];
199:
200: if (ch == '\t') {
201: virtualPosition += tabSize - virtualPosition % tabSize;
202: } else {
203: ++virtualPosition;
204: }
205: }
206:
207: return virtualPosition;
208: } //}}}
209:
210: //{{{ getOffsetOfVirtualColumn() method
211: /**
212: * Returns the array offset of a virtual column number (taking tabs
213: * into account) in the segment.
214: *
215: * @param seg The segment
216: * @param tabSize The tab size
217: * @param column The virtual column number
218: * @param totalVirtualWidth If this array is non-null, the total
219: * virtual width will be stored in its first location if this method
220: * returns -1.
221: *
222: * @return -1 if the column is out of bounds
223: */
224: public static int getOffsetOfVirtualColumn(Segment seg,
225: int tabSize, int column, int[] totalVirtualWidth) {
226: int virtualPosition = 0;
227:
228: for (int i = 0; i < seg.count; i++) {
229: char ch = seg.array[seg.offset + i];
230:
231: if (ch == '\t') {
232: int tabWidth = tabSize - virtualPosition % tabSize;
233: if (virtualPosition >= column)
234: return i;
235: else
236: virtualPosition += tabWidth;
237: } else {
238: if (virtualPosition >= column)
239: return i;
240: else
241: ++virtualPosition;
242: }
243: }
244:
245: if (totalVirtualWidth != null)
246: totalVirtualWidth[0] = virtualPosition;
247: return -1;
248: } //}}}
249:
250: //{{{ compareStrings() method
251: /**
252: * Compares two strings.<p>
253: *
254: * Unlike <function>String.compareTo()</function>,
255: * this method correctly recognizes and handles embedded numbers.
256: * For example, it places "My file 2" before "My file 10".<p>
257: *
258: * @param str1 The first string
259: * @param str2 The second string
260: * @param ignoreCase If true, case will be ignored
261: * @return negative If str1 < str2, 0 if both are the same,
262: * positive if str1 > str2
263: * @since jEdit 4.3pre5
264: */
265: public static int compareStrings(String str1, String str2,
266: boolean ignoreCase) {
267: char[] char1 = str1.toCharArray();
268: char[] char2 = str2.toCharArray();
269:
270: int len = Math.min(char1.length, char2.length);
271:
272: for (int i = 0, j = 0; i < len && j < len; i++, j++) {
273: char ch1 = char1[i];
274: char ch2 = char2[j];
275: if (Character.isDigit(ch1) && Character.isDigit(ch2)
276: && ch1 != '0' && ch2 != '0') {
277: int _i = i + 1;
278: int _j = j + 1;
279:
280: for (; _i < char1.length; _i++) {
281: if (!Character.isDigit(char1[_i])) {
282: //_i--;
283: break;
284: }
285: }
286:
287: for (; _j < char2.length; _j++) {
288: if (!Character.isDigit(char2[_j])) {
289: //_j--;
290: break;
291: }
292: }
293:
294: int len1 = _i - i;
295: int len2 = _j - j;
296: if (len1 > len2)
297: return 1;
298: else if (len1 < len2)
299: return -1;
300: else {
301: for (int k = 0; k < len1; k++) {
302: ch1 = char1[i + k];
303: ch2 = char2[j + k];
304: if (ch1 != ch2)
305: return ch1 - ch2;
306: }
307: }
308:
309: i = _i - 1;
310: j = _j - 1;
311: } else {
312: if (ignoreCase) {
313: ch1 = Character.toLowerCase(ch1);
314: ch2 = Character.toLowerCase(ch2);
315: }
316:
317: if (ch1 != ch2)
318: return ch1 - ch2;
319: }
320: }
321:
322: return char1.length - char2.length;
323: } //}}}
324:
325: //{{{ StringCompare class
326: /**
327: * Compares objects as strings.
328: */
329: public static class StringCompare implements Comparator {
330: public int compare(Object obj1, Object obj2) {
331: return compareStrings(obj1.toString(), obj2.toString(),
332: false);
333: }
334: } //}}}
335:
336: //{{{ objectsEqual() method
337: /**
338: * Returns if two strings are equal. This correctly handles null pointers,
339: * as opposed to calling <code>o1.equals(o2)</code>.
340: * @since jEdit 4.3pre6
341: */
342: public static boolean objectsEqual(Object o1, Object o2) {
343: if (o1 == null) {
344: if (o2 == null)
345: return true;
346: else
347: return false;
348: } else if (o2 == null)
349: return false;
350: else
351: return o1.equals(o2);
352: } //}}}
353:
354: //{{{ globToRE() method
355: /**
356: * Converts a Unix-style glob to a regular expression.<p>
357: *
358: * ? becomes ., * becomes .*, {aa,bb} becomes (aa|bb).
359: * @param glob The glob pattern
360: * @since jEdit 4.3pre7
361: */
362: public static String globToRE(String glob) {
363: if (glob.startsWith("(re)")) {
364: return glob.substring(4);
365: }
366:
367: final Object NEG = new Object();
368: final Object GROUP = new Object();
369: Stack state = new Stack();
370:
371: StringBuffer buf = new StringBuffer();
372: boolean backslash = false;
373:
374: for (int i = 0; i < glob.length(); i++) {
375: char c = glob.charAt(i);
376: if (backslash) {
377: buf.append('\\');
378: buf.append(c);
379: backslash = false;
380: continue;
381: }
382:
383: switch (c) {
384: case '\\':
385: backslash = true;
386: break;
387: case '?':
388: buf.append('.');
389: break;
390: case '.':
391: case '+':
392: case '(':
393: case ')':
394: buf.append('\\');
395: buf.append(c);
396: break;
397: case '*':
398: buf.append(".*");
399: break;
400: case '|':
401: if (backslash)
402: buf.append("\\|");
403: else
404: buf.append('|');
405: break;
406: case '{':
407: buf.append('(');
408: if (i + 1 != glob.length() && glob.charAt(i + 1) == '!') {
409: buf.append('?');
410: state.push(NEG);
411: } else
412: state.push(GROUP);
413: break;
414: case ',':
415: if (!state.isEmpty() && state.peek() == GROUP)
416: buf.append('|');
417: else
418: buf.append(',');
419: break;
420: case '}':
421: if (!state.isEmpty()) {
422: buf.append(")");
423: if (state.pop() == NEG)
424: buf.append(".*");
425: } else
426: buf.append('}');
427: break;
428: default:
429: buf.append(c);
430: }
431: }
432:
433: return buf.toString();
434: } //}}}
435:
436: //}}}
437: private StandardUtilities() {
438: }
439: }
|