content: /**
   *  Copyright (c) 2013-2016 Angelo ZERR.
   *  All rights reserved. This program and the accompanying materials
   *  are made available under the terms of the Eclipse Public License v1.0
   *  which accompanies this distribution, and is available at
   *  http://www.eclipse.org/legal/epl-v10.html
   *
   *  Contributors:
   *  Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
   */
  package tern.utils;
  
  /**
   * 
   * Utilities for {@link String}.
   * 
   */
  public class StringUtils {
  
  	private final static int[] EMPTY_REGIONS = new int[0];
  	
  	public static final String UTF_8 = "UTF-8";
  	
  	public static final String TRUE = "true";
  	public static final String FALSE = "false";
  	public static final String YES = "yes";
  	public static final String NO = "no";
  
  	public static final String[] EMPTY_ARRAY = new String[0];
  
  	private StringUtils() {
  	}
  
  	public static boolean isEmpty(String str) {
  		return str == null || str.length() == 0;
  	}
  
  	public static boolean hasLength(CharSequence str) {
  		return str != null && str.length() > 0;
  	}
  
  	public static boolean hasLength(String str) {
  		return hasLength(((CharSequence) (str)));
  	}
  
  	public static boolean hasText(CharSequence str) {
  		if (!hasLength(str))
  			return false;
  		int strLen = str.length();
  		for (int i = 0; i < strLen; i++)
  			if (!Character.isWhitespace(str.charAt(i)))
  				return true;
  
  		return false;
  	}
  
  	public static boolean hasText(String str) {
  		return hasText(((CharSequence) (str)));
  	}
  
  	public static boolean isQuoted(String string) {
  		if (string == null || string.length() < 2)
  			return false;
  		int lastIndex = string.length() - 1;
  		char firstChar = string.charAt(0);
  		char lastChar = string.charAt(lastIndex);
  		return firstChar == '\'' && lastChar == '\'' || firstChar == '"'
  				&& lastChar == '"';
  	}
  
  	public static String normalizeSpace(String s) {
  		if (s == null) {
  			return null;
  		}
  		int len = s.length();
  		if (len < 1) {
  			return "";
  		}
  		int st = 0;
  		int off = 0; /* avoid getfield opcode */
  		char[] val = s.toCharArray(); /* avoid getfield opcode */
  		int count = s.length();
  
  		boolean parse = true;
  		char c;
  		while (parse) {
  			c = val[off + st];
  			parse = isParse(len, st, c);
  			if (parse) {
  				st++;
  			}
  		}
  		parse = true;
  		while ((st < len) && (val[off + len - 1] <= ' ')) {
  			c = val[off + len - 1];
  			parse = isParse(len, st, c);
  			if (parse) {
  				len--;
  			}
  		}
  		return ((st > 0) || (len < count)) ? s.substring(st, len) : s;
  	}
  
  	private static boolean isParse(int len, int st, char c) {
  		return (st < len) && (c == ' ' || c == '\r' || c == '\n' || c == '\t');
  	}
  
  	public static boolean asBoolean(String value) {
  		return asBoolean(value, false);
  	}
  
  	public static boolean asBoolean(String value, boolean defaultValue) {
  		if (value == null)
  			return defaultValue;
  		value = value.trim();
  		if (defaultValue)
  			return !(FALSE.equals(value.toLowerCase()) || NO.equals(value
  					.toLowerCase()));
  		return TRUE.equals(value.toLowerCase())
  				|| YES.equals(value.toLowerCase());
  	}
  
  	// ----------- copied from JDT https://github.com/eclipse/eclipse.jdt.core/blob/master/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/StringOperation.java
  	
  	/**
  	 * Answers all the regions in a given name matching a given <i>pattern</i>
  	 * pattern (e.g. "H*M??").
  	 * </p><p>
  	 * Each of these regions is made of its starting index and its length in the given
  	 * name. They are all concatenated in a single array of <code>int</code>
  	 * which therefore always has an even length.
  	 * </p><p>
  	 * Note that each region is disjointed from the following one.<br>
  	 * E.g. if the regions are <code>{ start1, length1, start2, length2 }</code>,
  	 * then <code>start1+length1</code> will always be smaller than
  	 * <code>start2</code>.
  	 * </p><p>
  	 * <pre>
  	 * Examples:
  	 * <ol>
  	 * <li>  pattern = "N???Po*Ex?eption"
  	 *  name = NullPointerException
  	 *  result:  { 0, 1, 4, 2, 11, 2, 14, 6 }</li>
  	 * <li>  pattern = "Ha*M*ent*"
  	 *  name = "HashMapEntry"
  	 *  result:  { 0, 2, 4, 1, 7, 3 }</li>
  	 * </ol></pre>
  	 *</p>
  	 * @see CharOperation#match(char[], char[], boolean) for more details on the
  	 * 	pattern match behavior
  	 *
  	 * @param pattern the given pattern
  	 * @param patternStart the given pattern start
  	 * @param patternEnd the given pattern end
  	 * @param name the given name
  	 * @param nameStart the given name start
  	 * @param nameEnd the given name end
  	 * @param isCaseSensitive flag to know if the matching should be case sensitive
  	 * @return an array of <code>int</code> having two slots per returned
  	 * 	regions (first one is the starting index of the region and the second
  	 * 	one the length of the region).<br>
  	 * 	Note that it may be <code>null</code> if the given name does not match
  	 * 	the pattern
  	 * @since 3.5
  	 */
  	public static final int[] getPatternMatchingRegions(
  		String pattern,
  		int patternStart,
  		int patternEnd,
  		String name,
  		int nameStart,
  		int nameEnd,
  		boolean isCaseSensitive) {
  
  		/* !!!!!!!!!! WARNING !!!!!!!!!!
  		 * The algorithm used in this method has been fully inspired from
  		 * CharOperation#match(char[], int, int, char[], int, int, boolean).
  		 *
  		 * So, if any change needs to be applied in the algorithm, do NOT forget
  		 * to backport it in the CharOperation method!
  		 */
  
  		if (name == null) return null; // null name cannot match
  		if (pattern == null) {
  			// null pattern cannot match any region
  			// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=264816
  			return EMPTY_REGIONS;
  		}
  		int iPattern = patternStart;
  		int iName = nameStart;
  
  		// init segments parts
  		if (patternEnd < 0)
  			patternEnd = pattern.length();
  		if (nameEnd < 0)
  			nameEnd = name.length();
  		int questions = 0;
  		int parts = 0;
  		char previous = 0;
  		for (int i=patternStart; i<patternEnd; i++) {
  			char ch = pattern.charAt(i);
  			switch (ch) {
  				case '?':
  					questions++;
  					break;
  				case '*':
  					break;
  				default:
  					switch (previous) {
  						case 0 :
  						case '?':
  						case '*':
  							parts++;
  							break;
  					}
  			}
  			previous = ch;
  		}
  		if (parts == 0) {
  			if (questions <= (nameEnd - nameStart)) return EMPTY_REGIONS;
  			return null;
  		}
  		int[] segments = new int[parts*2];
  
  		/* check first segment */
  		int count = 0;
  		int start = iName;
  		char patternChar = 0;
  		previous = 0;
  		while ((iPattern < patternEnd)
  			&& (patternChar = pattern.charAt(iPattern)) != '*') {
  			if (iName == nameEnd)
  				return null;
  			if (patternChar == '?') {
  				switch (previous) {
  					case 0:
  					case '?':
  						break;
  					default:
  						segments[count++] = start;
  						segments[count++] = iPattern-start;
  						break;
  				}
  			} else {
  				if (isCaseSensitive) {
  					if (patternChar != name.charAt(iName)) {
  						return null;
  					}
  				} else if (toLowerCase(patternChar) != toLowerCase(name.charAt(iName))) {
  					return null;
  				}
  				switch (previous) {
  					case 0:
  					case '?':
  						start = iPattern;
  						break;
  				}
  			}
  			iName++;
  			iPattern++;
  			previous = patternChar;
  		}
  		/* check sequence of star+segment */
  		int segmentStart;
  		if (patternChar == '*') {
  			if (iPattern > 0 && previous != '?') {
  				segments[count++] = start;
  				segments[count++] = iName-start;
  				start = iName;
  			}
  			segmentStart = ++iPattern; // skip star
  		} else {
  			if (iName == nameEnd) {
  				if (count == (parts*2)) return segments;
  				int end = patternEnd;
  				if (previous == '?') { // last char was a '?' => purge all trailing '?'
  					while (pattern.charAt(--end-1) == '?') {
  						if (end == start) {
  							return new int[] { patternStart, patternEnd-patternStart };
  						}
  					}
  				}
  				return new int[] { start, end-start };
  			}
  			return null;
  		}
  		int prefixStart = iName;
  		int previousCount = count;
  		previous = patternChar;
  		char previousSegment = patternChar;
  		checkSegment : while (iName < nameEnd) {
  			if (iPattern == patternEnd) {
  				iPattern = segmentStart; // mismatch - restart current segment
  				iName = ++prefixStart;
  				previous = previousSegment;
  				continue checkSegment;
  			}
  			/* segment is ending */
  			if ((patternChar = pattern.charAt(iPattern)) == '*') {
  				segmentStart = ++iPattern; // skip star
  				if (segmentStart == patternEnd) {
  					if (count < (parts*2)) {
  						segments[count++] = start;
  						segments[count++] = iName-start;
  					}
  					return segments;
  				}
  				switch (previous) {
  					case '*':
  					case '?':
  						break;
  					default:
  						segments[count++] = start;
  						segments[count++] = iName-start;
  						break;
  				}
  				prefixStart = iName;
  				start = prefixStart;
  				previous = patternChar;
  				previousSegment = patternChar;
  				continue checkSegment;
  			}
  			/* check current name character */
  			previousCount = count;
  			if (patternChar == '?') {
  				switch (previous) {
  					case '*':
  					case '?':
  						break;
  					default:
  						segments[count++] = start;
  						segments[count++] = iName-start;
  						break;
  				}
  			} else {
  				boolean mismatch;
  				if (isCaseSensitive) {
  					mismatch = name.charAt(iName) != patternChar;
  				} else {
  					mismatch = toLowerCase(name.charAt(iName)) != toLowerCase(patternChar);
  				}
  				if (mismatch) {
  					iPattern = segmentStart; // mismatch - restart current segment
  					iName = ++prefixStart;
  					start = prefixStart;
  					count = previousCount;
  					previous = previousSegment;
  					continue checkSegment;
  				}
  				switch (previous) {
  					case '?':
  						start = iName;
  						break;
  				}
  			}
  			iName++;
  			iPattern++;
  			previous = patternChar;
  		}
  
  		if ((segmentStart == patternEnd)
  			|| (iName == nameEnd && iPattern == patternEnd)
  			|| (iPattern == patternEnd - 1 && pattern.charAt(iPattern) == '*')) {
  			if (count < (parts*2)) {
  				segments[count++] = start;
  				segments[count++] = iName-start;
  			}
  			return segments;
  		}
  		return null;
  	}
  
  	private static char toLowerCase(char c) {
  		return Character.toLowerCase(c);
  	}
  }
  