Java tutorial
/* * Copyright (C) 2008 feilong * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sunchenbin.store.feilong.core.lang; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.regex.Pattern; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.text.StrSubstitutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sunchenbin.store.feilong.core.bean.ConvertUtil; import com.sunchenbin.store.feilong.core.bean.IntrospectorUtil; import com.sunchenbin.store.feilong.core.tools.slf4j.Slf4jUtil; import com.sunchenbin.store.feilong.core.util.Validator; /** * {@link String},? ,?,format,?16?. * * <h3>(split)</h3> * * <blockquote> * <ul> * <li>{@link #split(String, String)}</li> * </ul> * </blockquote> * * <h3>(tokenize)</h3> <blockquote> * <ul> * <li>{@link #tokenizeToStringArray(String, String)}</li> * <li>{@link #tokenizeToStringArray(String, String, boolean, boolean)}</li> * </ul> * * <p> * ,split ? {@link Pattern#split(CharSequence)} (?, $|()[{^?*+\\ ???), {@link StringTokenizer} , * StringTokenizer<br> * ,?,{@link StringTokenizer} * </p> * </blockquote> * * <h3>{@link String#String(byte[] )} {@link String#String(byte[], Charset)} </h3> * * <blockquote> * <p> * {@link String#String(byte[] )} {@link String#String(byte[], Charset)}; {@link Charset#defaultCharset()}, ? ISO-8859-1, * ?? {@link java.lang.StringCoding#decode(byte[], int, int)} * </p> * </blockquote> * * <h3>{@link StringBuffer} && {@link StringBuilder} && {@link String} </h3> * * <blockquote> * <ul> * <li>{@link StringBuffer} ??</li> * <li>{@link StringBuilder} ???</li> * <li>{@link String} ?</li> * <li> {@link StringBuffer} {@code >} {@link String}</li> * <li> {@link StringBuilder} {@code >} {@link StringBuffer}</li> * </ul> * </blockquote> * * @author feilong * @version 1.4.0 201583 ?3:06:20 * @see "org.springframework.util.StringUtils#tokenizeToStringArray(String, String)" * @see "org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#MULTI_VALUE_ATTRIBUTE_DELIMITERS" * @see java.util.StringTokenizer * @see org.apache.commons.lang3.StringUtils * @since 1.4.0 */ public final class StringUtil { /** The Constant LOGGER. */ private static final Logger LOGGER = LoggerFactory.getLogger(StringUtil.class); /** Don't let anyone instantiate this class. */ private StringUtil() { //AssertionError?. ?????. ???. //see Effective Java 2nd throw new AssertionError("No " + getClass().getName() + " instances for you!"); } /** * Constructs a new <code>String</code> by decoding the specified array of bytes using the given charset. * * @param bytes * The bytes to be decoded into characters, may be {@code null} * @param charsetType * {@link CharsetType} * @return A new <code>String</code> decoded from the specified array of bytes using the given charset, * or {@code null} if the input byte array was {@code null}. * @see String#String(byte[], String) * @see org.apache.commons.lang3.StringUtils#toString(byte[], String) * @see org.apache.commons.lang3.StringUtils#toEncodedString(byte[], Charset) * @see "org.apache.commons.codec.binary.StringUtils#newString(byte[], String)" * @since 1.3.0 */ public static String newString(byte[] bytes, String charsetType) { return StringUtils.toEncodedString(bytes, Charset.forName(charsetType)); } // [start] search /** * ? (<code>target</code>) ( <code>source</code>). * * <pre> * StringUtil.searchTimes("xin", "xin") * return 1 * * StringUtil.searchTimes("xiiiin", "ii") * return 2 * * </pre> * * @param source * ? * @param target * ? * @return count of target string in source * @see org.apache.commons.lang3.StringUtils#countMatches(CharSequence, CharSequence) * @since 1.0.2 */ public static int searchTimes(final CharSequence source, final CharSequence target) { return org.apache.commons.lang3.StringUtils.countMatches(source, target); } // [end] /** * ???. * * <pre> * String text = "jinxin.feilong"; * LOGGER.info(StringUtil.addDoubleQuotes(text)); * * : "jinxin.feilong" * </pre> * * @param text * ? * @return "\"" + text + "\"" */ public static String addDoubleQuotes(String text) { return "\"" + text + "\""; } /** * ???. * * <p> * Example 1: jinxin ---> Jinxin * </p> * * <pre> * StringUtils.capitalize(null) = null * StringUtils.capitalize("") = "" * StringUtils.capitalize("cat") = "Cat" * StringUtils.capitalize("cAt") = "CAt" * </pre> * * @param word * ?? * @return ??? * @see org.apache.commons.lang3.StringUtils#swapCase(String) * @see org.apache.commons.lang3.StringUtils#capitalize(String) */ public static String firstCharToUpperCase(String word) { return StringUtils.capitalize(word); } /** * ????. * * <p> * Example 1: Jinxin ---> jinxin * </p> * * <pre> * StringUtils.capitalize(null) = null * StringUtils.capitalize("") = "" * StringUtils.capitalize("Jinxin") = "jinxin" * StringUtils.capitalize("CAt") = "cAt" * </pre> * * * <h3>?:</h3> * * <blockquote> * <ol> * <li> {@link IntrospectorUtil#decapitalize(String)} .</li> * <li>?,?????, ? {@link org.apache.commons.lang3.text.WordUtils#uncapitalize(String, char...)}</li> * </ol> * </blockquote> * * @param word * ?? * @return ???? * @see org.apache.commons.lang3.StringUtils#uncapitalize(String) */ public static String firstCharToLowerCase(String word) { return StringUtils.uncapitalize(word); } // [start]Contain /** * <code>seq</code> ?? <code>searchSeq</code>. * * @param seq * jinxin,the CharSequence to check, may be null * @param searchSeq * ? in,the CharSequence to find, may be null * @return ?true,text null false * @see org.apache.commons.lang3.StringUtils#contains(CharSequence, CharSequence) */ public static boolean contains(final CharSequence seq, final CharSequence searchSeq) { return org.apache.commons.lang3.StringUtils.contains(seq, searchSeq); } /** * ? ??. * * <pre> * StringUtil.containsIgnoreCase(null, "") return false * StringUtil.containsIgnoreCase(text, null) return false * StringUtil.containsIgnoreCase(text, "") return true * StringUtil.containsIgnoreCase(text, "feilong") return true * StringUtil.containsIgnoreCase(text, "feilong1") return false * StringUtil.containsIgnoreCase(text, "feiLong") return true * * </pre> * * @param str * the CharSequence to check, may be null * @param searchStr * the CharSequence to find, may be null * @return true if the CharSequence contains the search CharSequence irrespective of * case or false if not or {@code null} string input */ public static boolean containsIgnoreCase(final CharSequence str, final CharSequence searchStr) { return org.apache.commons.lang3.StringUtils.containsIgnoreCase(str, searchStr); } // [end] // [start]replace // ********************************replace************************************************ /** * replacement ????.. * * @param content * ?? * @param regex * ??? * @param replacement * ???? * @return String,?,"" */ public static String replaceAll(Object content, String regex, String replacement) { if (null == content) { return StringUtils.EMPTY; } return content.toString().replaceAll(regex, replacement); } /** * ????????. * * <p> * ??,, "b" ? "aaa" "aa" ? "ba" ? "ab". * </p> * * <pre> * ?replacement * </pre> * * @param content * * @param target * ?? char ? * @param replacement * char ?? * @return ???? */ public static String replace(Object content, String target, Object replacement) { if (null == content) { return StringUtils.EMPTY; } // ??null String useReplacement = Validator.isNullOrEmpty(replacement) ? StringUtils.EMPTY : replacement.toString(); return content.toString().replace(target, useReplacement); } /** * The following example demonstrates this: * * <pre> * Map valuesMap = HashMap(); * valuesMap.put("animal", "quick brown fox"); * valuesMap.put("target", "lazy dog"); * * StrSubstitutor sub = new StrSubstitutor(valuesMap); * String templateString = "The ${animal} jumped over the ${target}."; * String resolvedString = sub.replace(templateString); * </pre> * * yielding: * * <pre> * The quick brown fox jumped over the lazy dog. * </pre> * * @param <V> * the value type * @param templateString * the template string * @param valuesMap * the values map * @return the string * @see org.apache.commons.lang3.text.StrSubstitutor#replace(String) * @since 1.1.1 */ public static <V> String replace(String templateString, Map<String, V> valuesMap) { StrSubstitutor strSubstitutor = new StrSubstitutor(valuesMap); return strSubstitutor.replace(templateString); } // [end] // [start]startsWith /** * ??.. * * @param value * value * @param prefix * ? * @return ????, true? false.??,?, String equals(Object) , true. */ public static boolean startsWith(Object value, String prefix) { return ConvertUtil.toString(value).startsWith(prefix); } // [end] /** * ?,(???). * * <pre> * {@code * stringAddInt("002",2); return 004 * stringAddInt("000002",1200); return 001202 * } * </pre> * * @param str * ? 002 * @param i * * @return ?,(???). * @see NumberUtil#toString(Number, String) */ public static String stringAddInt(String str, int i) { int length = str.length(); String pattern = ""; for (int j = 0; j < length; ++j) { pattern += "0"; } return NumberUtil.toString(Integer.parseInt(str) + i, pattern); } // [start]substring // ********************************substring************************************************ /** * [?](beginIndex),. * * <p> * Gets a substring from the specified String avoiding exceptions. * </p> * * <p> * beginIndex ,??,?,? {@link #substringLast(String, int)} * </p> * * <p> * A {@code null} String will return {@code null}. An empty ("") String will return "". * </p> * * <pre> * StringUtil.substring(null, *) = null * StringUtil.substring("", *) = "" * StringUtil.substring("abc", 0) = "abc" * StringUtil.substring("abc", 2) = "c" * StringUtil.substring("abc", 4) = "" * StringUtil.substring("abc", -2) = "bc" * StringUtil.substring("abc", -4) = "abc" * </pre> * * <pre> * substring("jinxin.feilong",6) * return .feilong * </pre> * * @param text * the String to get the substring from, may be null * @param beginIndex * the position to start from,negative means count back from the end of the String by this many characters * @return substring from start position, {@code null} if null String input * @see org.apache.commons.lang3.StringUtils#substring(String, int) * @see #substringLast(String, int) */ public static String substring(final String text, final int beginIndex) { return org.apache.commons.lang3.StringUtils.substring(text, beginIndex); } /** * [?]?(startIndex),?(length). * * <pre> * StringUtil.substring("jinxin.feilong", 6, 2) * * renturn .f * </pre> * * @param textString * ? * @param startIndex * ?,0 * @param length * {@code >=1} 1 ? <br> * ,?? * @return the string * @see org.apache.commons.lang3.StringUtils#substring(String, int, int) */ public static String substring(final String textString, int startIndex, int length) { return org.apache.commons.lang3.StringUtils.substring(textString, startIndex, startIndex + length); } /** * [?]:?(?)??. * * <p> * {@link #substring(String, String, int)}, shift=0 ?? beginString. * </p> * * <pre> * substring("jinxin.feilong",".")======>".feilong" * </pre> * * @param text * text * @param beginString * beginString? * @return {@link #substring(String, String, int)}, shift=0 ?? beginString. * @see #substring(String, String, int) */ public static String substring(final String text, String beginString) { return substring(text, beginString, 0); } /** * [?]:?(?)??,shift?????. * * <h3>:</h3> * * <blockquote> * <ul> * <li>substring("jinxin.feilong",".",0)======>".feilong"</li> * <li>substring("jinxin.feilong",".",1)======>"feilong"</li> * </ul> * </blockquote> * * @param text * text * @param beginString * beginString * @param shift * ??,??,0?? * @return <ul> * <li>if isNullOrEmpty(text),return null</li> * <li>if isNullOrEmpty(beginString),return null</li> * <li>if text.indexOf(beginString)==-1,return null</li> * <li>{@code beginIndex + shift > text.length()},return null</li> * <li>else,return text.substring(beginIndex + shift)</li> * </ul> * @see org.apache.commons.lang3.StringUtils#substringAfter(String, String) */ public static String substring(final String text, String beginString, int shift) { if (Validator.isNullOrEmpty(text)) { return StringUtils.EMPTY; } if (Validator.isNullOrEmpty(beginString)) { return StringUtils.EMPTY; } //**************************************************** int beginIndex = text.indexOf(beginString); if (beginIndex == -1) {// ? return StringUtils.EMPTY; } //**************************************************** int startIndex = beginIndex + shift; int textLength = text.length(); if (startIndex < 0) { String pattern = "beginIndex+shift<0,beginIndex:{},shift:{},text:{},text.length:{}"; throw new IllegalArgumentException( Slf4jUtil.formatMessage(pattern, beginIndex, shift, text, textLength)); } if (startIndex > textLength) { LOGGER.warn("beginIndex+shift>text.length(),beginIndex:{},shift:{},text:{},text.length:{}", beginIndex, shift, text, textLength); return StringUtils.EMPTY; } // 0 return text.substring(startIndex); } /** * [?]:?. * * <p> * ?startString,???endString. * </p> * * @param text * * @param startString * ,null? * @param endString * ? * @return * <ul> * <li>Validator.isNullOrEmpty(text),return null;</li> * <li>Validator.isNullOrEmpty(startString),return text.substring(0, text.indexOf(endString))</li> * </ul> * @see org.apache.commons.lang3.StringUtils#substringBetween(String, String, String) */ public static String substring(final String text, final String startString, final String endString) { if (Validator.isNullOrEmpty(text)) { return StringUtils.EMPTY; } if (Validator.isNullOrEmpty(startString)) { return text.substring(0, text.indexOf(endString)); } int beginIndex = text.indexOf(startString); int endIndex = text.indexOf(endString); return text.substring(beginIndex, endIndex); } /** * [?]:???. * * <p> * {@link String#substring(int)} * </p> * * <pre> * Example 1: * {@code * StringUtil.substringLast("jinxin.feilong", 5)---->ilong * } * </pre> * * @param text * * @param lastLenth * ?? * @return ?? * @see java.lang.String#substring(int) */ public static String substringLast(final String text, int lastLenth) { return text.substring(text.length() - lastLenth); } /** * [?]:??. * * <p> * {@link java.lang.String#substring(int, int)} * </p> * * <pre> * Example 1: * {@code * StringUtil.substringWithoutLast("jinxin.feilong", 5)---->jinxin.fe * } * </pre> * * @param text * * @param lastLenth * ?? * @return ??,text,"" * @see java.lang.String#substring(int, int) * @see org.apache.commons.lang3.StringUtils#left(String, int) */ public static String substringWithoutLast(final String text, final int lastLenth) { if (Validator.isNullOrEmpty(text)) { return StringUtils.EMPTY; } return text.substring(0, text.length() - lastLenth); } /** * [?]:?(<code>text</code> <code>lastString</code>). * * @param text * the text * @param lastString * the last string * @return the string * @since 1.4.0 */ public static String substringWithoutLast(final CharSequence text, final String lastString) { if (Validator.isNullOrEmpty(text)) { return StringUtils.EMPTY; } //?,??nullempty,?,,,???? String returnValue = text.toString(); if (Validator.isNullOrEmpty(lastString)) { return returnValue; } if (returnValue.endsWith(lastString)) { //? return substringWithoutLast(returnValue, lastString.length()); } return returnValue; } // [end] // [start]toBytes // ******************************************************************************** /** * ??byte. * * @param value * * @return byte * @since 1.3.0 */ public static byte[] getBytes(String value) { return value.getBytes(); } /** * ??byte. * * @param value * * @param charsetName * ?? charset ??, utf-8, {@link CharsetType} * @return byte * @see String#getBytes(String) * @see CharsetType * @since 1.3.0 */ public static byte[] getBytes(String value, String charsetName) { try { return value.getBytes(charsetName); } catch (UnsupportedEncodingException e) { LOGGER.error(e.getClass().getName(), e); } return ArrayUtils.EMPTY_BYTE_ARRAY; } // [end] // [start]splitToT /** * ? . * * <h3>?</h3> * * <blockquote> * <p> * ????, .$|()[{^?*+\\ ??,.,<br> * <span style="color:red">"\"??"\\\\"</span> <br> * {@link java.util.regex.Pattern#split(CharSequence)} * </p> * </blockquote> * * {@link #tokenizeToStringArray(String, String)} {@link StringUtils#split(String)} * * @param value * value * @param regexSpliter * ,????, .$|()[{^?*+\\ ??,.,<br> * <span style="color:red">"\"??"\\\\"</span> <br> * {@link java.util.regex.Pattern#split(CharSequence)} * @return value null,null * @see String#split(String) * @see String#split(String, int) * @see java.util.regex.Pattern#split(CharSequence) * @see StringUtils#split(String) * @see #tokenizeToStringArray(String, String) */ public static String[] split(String value, String regexSpliter) { if (Validator.isNullOrEmpty(value)) { return ArrayUtils.EMPTY_STRING_ARRAY; } return value.split(regexSpliter); } // [end] // [start]tokenizeToStringArray /** * ( {@link "org.springframework.util.StringUtils#tokenizeToStringArray"}). * * <p> * Tokenize the given String into a String array via a StringTokenizer. <br> * Trims tokens and omits empty tokens. * </p> * * <p> * The given delimiters string is supposed to consist of any number of delimiter characters. Each of those characters can be used to * separate tokens. A delimiter is always a single character; for multi-character delimiters, consider using * {@code delimitedListToStringArray} * * @param str * the String to tokenize * @param delimiters * the delimiter characters, assembled as String<br> * ?,?? ";, " ,spring?/? * @return an array of the tokens * @see java.util.StringTokenizer * @see String#trim() * @see "org.springframework.util.StringUtils#delimitedListToStringArray" * @see "org.springframework.util.StringUtils#tokenizeToStringArray" * @since 1.0.7 */ public static String[] tokenizeToStringArray(String str, String delimiters) { boolean trimTokens = true; boolean ignoreEmptyTokens = true; return tokenizeToStringArray(str, delimiters, trimTokens, ignoreEmptyTokens); } /** * ( {@link "org.springframework.util.StringUtils#tokenizeToStringArray"}). * <p> * Tokenize the given String into a String array via a StringTokenizer. * </p> * <p> * The given delimiters string is supposed to consist of any number of delimiter characters. <br> * Each of those characters can be used to separate tokens. <br> * A delimiter is always a single character; <br> * for multi-character delimiters, consider using {@code delimitedListToStringArray} * * @param str * the String to tokenize * @param delimiters * the delimiter characters, assembled as String<br> * ?,?? ";, " ,spring?/? * @param trimTokens * ? {@link String#trim()}?token * @param ignoreEmptyTokens * ?token,true,token {@code >} 0;false?=0 <br> * omit empty tokens from the result array * (only applies to tokens that are empty after trimming; StringTokenizer * will not consider subsequent delimiters as token in the first place). * @return an array of the tokens ({@code null} if the input String * was {@code null}) * @see java.util.StringTokenizer * @see String#trim() * @see "org.springframework.util.StringUtils#delimitedListToStringArray" * @see "org.springframework.util.StringUtils#tokenizeToStringArray" * @since 1.0.7 */ public static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { if (null == str) { return ArrayUtils.EMPTY_STRING_ARRAY; } //StringTokenizer implements Enumeration<Object> // Enumeration?, hasMoreTokens nextToken //Enumeration? hasMoreElements nextElement hasMoreTokens nextToken StringTokenizer st = new StringTokenizer(str, delimiters); List<String> tokens = new ArrayList<String>(); while (st.hasMoreTokens()) { String token = st.nextToken(); if (trimTokens) { token = token.trim(); } if (!ignoreEmptyTokens || token.length() > 0) { tokens.add(token); } } return ConvertUtil.toArray(tokens, String.class); } // [end] // [start]format /** * ?. * * <ul> * <li>StringUtil.format("%03d", 1)?? StringUtil.format("%03d", "1")</li> * </ul> * * <p> * %index$,index1?,index???.<br> * ?:?4?:%[index$][][?]??<br> * ?:%[index$][][][.]??<br> * ?? ?<br> * </p> * * <h3>?</h3> * * <blockquote> * <table border="1" cellspacing="0" cellpadding="4"> * <tr style="background-color:#ccccff"> * <th align="left">?</th> * <th align="left"></th> * <th align="left"></th> * </tr> * <tr valign="top"> * <td>%s</td> * <td></td> * <td>"mingrisoft"</td> * </tr> * <tr valign="top" style="background-color:#eeeeff"> * <td>%c</td> * <td></td> * <td>'m'</td> * </tr> * <tr valign="top"> * <td>%b</td> * <td></td> * <td>true</td> * </tr> * <tr valign="top" style="background-color:#eeeeff"> * <td>%d</td> * <td>??</td> * <td>99</td> * </tr> * <tr valign="top"> * <td>%x</td> * <td>??</td> * <td>FF</td> * </tr> * <tr valign="top" style="background-color:#eeeeff"> * <td>%o</td> * <td></td> * <td>77</td> * </tr> * <tr valign="top"> * <td>%f</td> * <td></td> * <td>99.99</td> * </tr> * <tr valign="top" style="background-color:#eeeeff"> * <td>%a</td> * <td>??</td> * <td>FF.35AE</td> * </tr> * <tr valign="top"> * <td>%e</td> * <td></td> * <td>9.38e+5</td> * </tr> * <tr valign="top" style="background-color:#eeeeff"> * <td>%g</td> * <td>fe</td> * <td></td> * </tr> * <tr valign="top"> * <td>%h</td> * <td>?</td> * <td></td> * </tr> * <tr valign="top" style="background-color:#eeeeff"> * <td>%%</td> * <td></td> * <td></td> * </tr> * <tr valign="top"> * <td>%n</td> * <td>?</td> * <td></td> * </tr> * <tr valign="top" style="background-color:#eeeeff"> * <td>%tx</td> * <td>x???</td> * <td></td> * </tr> * </table> * </blockquote> * * * <h3></h3> * * <blockquote> * <table border="1" cellspacing="0" cellpadding="4"> * <tr style="background-color:#ccccff"> * <th align="left"></th> * <th align="left"></th> * <th align="left"></th> * <th align="left"></th> * </tr> * <tr valign="top"> * <td>+</td> * <td>?</td> * <td>("%+d",15)</td> * <td>+15</td> * </tr> * <tr valign="top" style="background-color:#eeeeff"> * <td>-</td> * <td>?(??0??)</td> * <td>("%-5d",15)</td> * <td>|15 |</td> * </tr> * <tr valign="top"> * <td>0</td> * <td>??0</td> * <td>("%04d", 99)</td> * <td>0099</td> * </tr> * <tr valign="top" style="background-color:#eeeeff"> * <td></td> * <td>??</td> * <td>("% 4d", 99)</td> * <td>| 99|</td> * </tr> * <tr valign="top"> * <td>,</td> * <td>,?</td> * <td>("%,f", 9999.99)</td> * <td>9,999.990000</td> * </tr> * <tr valign="top" style="background-color:#eeeeff"> * <td>(</td> * <td>??</td> * <td>("%(f", -99.99)</td> * <td>(99.990000)</td> * </tr> * <tr valign="top"> * <td>#</td> * <td>??,1680x0</td> * <td>("%#x", 99) <br> * ("%#o", 99)</td> * <td>0x63<br> * 0143</td> * </tr> * <tr valign="top" style="background-color:#eeeeff"> * <td>{@code <}</td> * <td>??????</td> * <td>("%f%{@code <}3.2f", 99.45)</td> * <td>99.45000099.45</td> * </tr> * <tr valign="top"> * <td>$</td> * <td>??</td> * <td>("%1$d,%2$s", 99,"abc")</td> * <td>99,abc</td> * </tr> * </table> * </blockquote> * * @param format * the format * @param args * the args * @return A formatted string * @see java.util.Formatter * @see String#format(String, Object...) * @see String#format(java.util.Locale, String, Object...) * @since JDK 1.5 */ public static String format(String format, Object... args) { return String.format(format, args); } // [end] }