All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.seasar.mayaa.impl.util.StringUtil Maven / Gradle / Ivy

Go to download

Mayaa is server side web template engine that is strongly aware of work sharing between programmers and designers based on HTML based templates.

The newest version!
/*
 * Copyright 2004-2012 the Seasar Foundation and the Others.
 *
 * 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 org.seasar.mayaa.impl.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.collections.map.AbstractReferenceMap;
import org.apache.commons.collections.map.ReferenceMap;
import org.seasar.mayaa.impl.cycle.script.ScriptUtil;
import org.seasar.mayaa.impl.knowledge.HTMLKnowledge;
import org.seasar.mayaa.impl.source.ClassLoaderSourceDescriptor;

/**
 * @author Masataka Kurihara (Gluegent, Inc.)
 */
public final class StringUtil {

    @SuppressWarnings("unchecked")
    private static Map _propFiles =
            Collections.synchronizedMap(new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true));
    private static final String[] ZERO = new String[0];

    private StringUtil() {
        // no instantiation.
    }

    /**
     * オブジェクトが空であることを判定する。
     * 

* 文字列なら{@link #isEmpty(String)}の結果と同じ。 * そうでなければ{@link ScriptUtil#isEmpty(Object)}の結果と同じ。 *

* @param test 判定対象のオブジェクト * @return オブジェクトが空と見なせるならtrue */ public static boolean isEmpty(Object test) { boolean result; if (test instanceof String) { result = isEmpty((String)test); } else { result = ScriptUtil.isEmpty(test); if (result == false) { result = isEmpty(test.toString()); } } return result; } /** * オブジェクトの文字列表現({@link Object#toString()}を返す。 * ただし{@code null}なら{@code null}のまま返す。 * @param value 対象オブジェクト * @return オブジェクトの文字列表現または{@code null} */ public static String valueOf(Object value) { if (value == null) { return null; } return value.toString(); } /** * testがnullまたは空文字列ならtrueを返します。 * * @param test 対象の文字列 * @return nullまたは空文字列ならtrue */ public static boolean isEmpty(String test) { return test == null || test.length() == 0; } public static boolean hasValue(Object test) { return isEmpty(test) == false; } public static boolean hasValue(String test) { return isEmpty(test) == false; } /** * パスの形式統一処理をしたものを返します。 * 区切り文字を"/"に変え、末尾の"/"を取り除き、先頭が"/"でなければ * "/"始まりにします。 * pathがnullまたは空文字列、trim()で空文字列になる場合は空文字列を返します。 * * @param path 処理対象のパス * @return 処理後のパス */ public static String preparePath(final String path) { if (isEmpty(path)) { return ""; } char[] chars = toTrimedCharArray(path); if (chars.length == 0) { return ""; } int length = chars.length; if (File.separatorChar != '/') { for (int i = 0; i < length; i++) { if (chars[i] == File.separatorChar) { chars[i] = '/'; } } } if (chars[length - 1] == '/') { length = length - 1; } if (chars[0] != '/') { char[] extended = new char[length + 1]; System.arraycopy(chars, 0, extended, 1, length); extended[0] = '/'; length = length + 1; chars = extended; } return new String(chars, 0, length); } /** * {@link String#trim()}後に{@link String#toCharArray()}したものと * 同じ、前後の空白を除去したchar配列を返す。 * * @param value 処理対象の文字列 * @return 前後の空白を除去したchar配列 */ public static char[] toTrimedCharArray(String value) { if (value == null) { return null; } char[] chars = value.toCharArray(); int end = chars.length; int start = 0; while ((start < end) && (chars[start] <= ' ')) { start++; } while ((start < end) && (chars[end - 1] <= ' ')) { end--; } if (start > 0 || end < chars.length) { int length = end - start; char[] result = new char[length]; System.arraycopy(chars, start, result, 0, length); return result; } return chars; } /** * &xx;表記のエンティティを実体に置き換えて返します。 * * @param blockString 置き換え処理対象の文字列 * @return 実体に置き換えた後の文字列 */ public static String resolveEntity(String blockString) { StringBuilder buffer = new StringBuilder(); int start = blockString.indexOf("&"); if (start == -1) { return blockString; } buffer.append(blockString.substring(0, start)); String entity; while (true) { int end = blockString.indexOf(";", start); if (end == -1) { buffer.append(blockString.substring(start)); break; } entity = blockString.substring(start + 1, end); Character value = HTMLKnowledge.convertHTMLEntitiy(entity); if (value != null) { buffer.append((char) value); } else { buffer.append(blockString.substring(start, end + 1)); } start = blockString.indexOf("&", end); if (start == -1) { buffer.append(blockString.substring(end + 1)); break; } if (start != end + 1) { buffer.append(blockString.substring(end + 1, start)); } if (start == blockString.length()) { break; } } return buffer.toString(); } /** * URLをパス、クエリー文字列、フラグメントの3つに分割し、配列で返す。 *

* それぞれ存在しないところには空文字列が入る。また、テンプレートサフィックスは除去する。 * (/foo/bar.html?query=value&query2=value2#fragment → ["/foo/bar.html", "query=value&query2=value2", "fragment"]) * (/foo/bar.html → ["/foo/bar.html", "", ""]) * (/foo/bar$foo.html → ["/foo/bar.html", "", ""]) *

* @param value 元URL * @param suffixSeparator サフィックスの区切り文字 * @return パスを分割したもの。[0]:パス, [1]:クエリー文字列, [2]:フラグメント */ public static String[] parseURIQuery(String value, String suffixSeparator) { String[] result = new String[3]; String path; int fIndex = value.indexOf('#'); if (fIndex >= 0) { path = value.substring(0, fIndex); result[2] = value.substring(fIndex + 1); } else { path = value; result[2] = ""; } int qIndex = path.indexOf('?'); if (qIndex >= 0) { result[0] = path.substring(0, qIndex); result[1] = path.substring(qIndex + 1); } else { result[0] = path; result[1] = ""; } int suffixIndex = result[0].indexOf(suffixSeparator); int extensionIndex = result[0].lastIndexOf('.'); if (suffixIndex >= 0 && extensionIndex > suffixIndex) { result[0] = result[0].substring(0, suffixIndex) + result[0].substring(extensionIndex); // Apacheは$が変数用記号となるので\\でエスケープする必要あり。その場合のためにsuffix直前の\\を除去する。 if (suffixSeparator.equals("$") && result[0].charAt(suffixIndex - 1) == '\\') { result[0] = result[0].substring(0, suffixIndex - 1) + result[0].substring(suffixIndex); } } return result; } /** * パスをフォルダおよびファイル、サフィックス、拡張子の3つに分割し、配列で返す。 *

* それぞれ存在しないところには空文字列が入る。 * (/foo/bar$suffix.html → ["/foo/bar", "suffix", "html"]) * (/foo/bar → ["/foo/bar", "", ""]) *

* @param path 元パス文字列 * @param suffixSeparator パスサフィックスのセパレータ * @return パスを分割したもの。[0]:パス, [1]:サフィックス, [2]:拡張子 */ public static String[] parsePath(String path, String suffixSeparator) { path = path.trim(); String[] ret = new String[3]; int paramOffset = path.indexOf('?'); if (paramOffset >= 0) { path = path.substring(0, paramOffset); } int lastSlashOffset = path.lastIndexOf('/'); String folder = ""; String file = path; if (lastSlashOffset >= 0) { folder = path.substring(0, lastSlashOffset + 1); file = path.substring(lastSlashOffset + 1); } int lastDotOffset = file.lastIndexOf('.'); if (lastDotOffset > 0) { ret[2] = file.substring(lastDotOffset + 1); file = file.substring(0, lastDotOffset); } else { ret[2] = ""; } int suffixSeparatorOffset = file.lastIndexOf(suffixSeparator); if (suffixSeparatorOffset > 0) { ret[0] = folder + file.substring(0, suffixSeparatorOffset); ret[1] = file.substring(suffixSeparatorOffset + suffixSeparator.length()); } else { ret[0] = folder + file; ret[1] = ""; } return ret; } public static boolean isRelativePath(String path) { if (isEmpty(path)) { return false; } return path.startsWith("./"); } /** * ファイル相対パスをコンテキスト相対パスにする。 * * @param base リンクのあるページのコンテキスト相対パス ("/"始まり) * @param path リンクに書かれているパス * @return コンテキスト相対パス */ public static String adjustRelativePath(String base, String path) { if (isRelativePath(path) == false) { return path; } if (isEmpty(base)) { throw new IllegalArgumentException(); } try { String baseDir = base.substring(0, base.lastIndexOf('/')); return adjustRecursive(baseDir, path); } catch (StringIndexOutOfBoundsException e) { throw new RuntimeException(e); } } protected static String adjustRecursive(String dir, String path) { if (isEmpty(path)) { return dir + '/'; } try { if (path.startsWith("../")) { if (isEmpty(dir)) { return adjustRecursive(dir, path.substring(3)); } String parentDir = dir.substring(0, dir.lastIndexOf('/')); return adjustRecursive(parentDir, path.substring(3)); } else if (path.startsWith("./")) { return adjustRecursive(dir, path.substring(2)); } return dir + '/' + path; } catch (StringIndexOutOfBoundsException e) { throw new RuntimeException(e); } } /** * コンテキスト相対パスからファイル相対パスにする。 * hostPageからpathへたどるための相対パスを返す。 * * @param hostPage リンクのあるページのコンテキスト相対パス ("/"始まり) * @param path リンクのコンテキスト相対パス ("/"始まり) * @return hostPageからpathへたどるための相対パス */ public static String adjustContextRelativePath(String hostPage, String path) { if (isEmpty(hostPage) || isEmpty(path) || (path.startsWith("/") == false)) { return path; } String[] hostPath = hostPage.split("/"); String[] linkPath = path.split("/"); int matchedDirs = 1;// 最初は空なので1から while (hostPath.length > matchedDirs && linkPath.length > matchedDirs && hostPath[matchedDirs].equals(linkPath[matchedDirs])) { matchedDirs += 1; } int depth = hostPath.length - (matchedDirs + 1); StringBuilder sb = new StringBuilder(); sb.append(times("../", depth)); for (int i = matchedDirs; linkPath.length > i; i++) { sb.append(linkPath[i]); sb.append('/'); } if (path.charAt(path.length() - 1) != '/') { sb.delete(sb.length() - 1, sb.length()); } return sb.toString(); } /** * unitをcount回繰り返した文字列を返す。 * unitがnull、あるいはcountが0以下ならば空文字列を返す。 * * @param unit 繰り返す文字列 * @param count 繰り返す回数 * @return unitをcount回繰り返した文字列。 */ public static String times(String unit, int count) { if (count <= 0 || isEmpty(unit)) { return ""; } StringBuilder sb = new StringBuilder(unit.length() * count); for (int i = 0; i < count; i++) { sb.append(unit); } return sb.toString(); } public static String removeFileProtocol(String systemID) { if (hasValue(systemID) && systemID.startsWith("file://")) { return systemID.substring(7); } return systemID; } private static final String SP_OPEN = "${"; private static final String SP_CLOSE = "}"; public static boolean hasSystemProperties(String value) { int openIndex = value.indexOf(SP_OPEN); int closeIndex = value.lastIndexOf(SP_CLOSE); return (openIndex >= 0 && closeIndex >= 0 && openIndex < closeIndex); } /** * システムプロパティを置換する。 *

* "${user.home}"のような文字列があった場合、{@link System#getProperty(String)}を使って * 値を取得して置換する。(この場合は"user.home"がキーとなる) *

* @param value 処理対象の文字列 * @return システムプロパティを置換した後の文字列。システムプロパティの記述がなければ元の文字列。 * @throws IllegalStateException システムプロパティが{@code null}の場合に発生する。 */ public static String replaceSystemProperties(String value) { if (hasSystemProperties(value) == false) { return value; } StringBuilder buffer = new StringBuilder(value.length() + 100); int openIndex = value.indexOf(SP_OPEN); buffer.append(value.substring(0, openIndex)); while (openIndex < value.length()) { int fromIndex = openIndex + SP_OPEN.length(); int closeIndex = value.indexOf(SP_CLOSE, fromIndex); if (closeIndex >= 0) { String key = value.substring(fromIndex, closeIndex).trim(); String propertyValue = System.getProperty(key); if (propertyValue == null) { throw new IllegalStateException(key); } buffer.append(propertyValue); int lastIndex = closeIndex + SP_CLOSE.length(); openIndex = value.indexOf(SP_OPEN, lastIndex); if (openIndex < 0) { openIndex = value.length(); } buffer.append(value.substring(lastIndex, openIndex)); } else { buffer.append(value.substring(openIndex)); openIndex = value.length(); } } return buffer.toString(); } public static String getMessage(Class clazz, int index) { return getMessage(clazz, index, ZERO); } public static String getMessage(Class clazz, int index, String param0) { return getMessage(clazz, index, new String[] { param0 }); } public static String getMessage(Class clazz, int index, String param0, String param1) { return getMessage(clazz, index, new String[] { param0, param1 }); } public static String getMessage(Class clazz, int index, String param0, String param1, String param2) { return getMessage(clazz, index, new String[] { param0, param1, param2 }); } protected static String getMessage( Class clazz, int index, String[] params) { Package key = clazz.getPackage(); Properties properties = _propFiles.get(key); if (properties == null) { ClassLoaderSourceDescriptor source = new ClassLoaderSourceDescriptor(); source.setSystemID("message.properties"); source.setNeighborClass(clazz); properties = new Properties(); _propFiles.put(key, properties); if (source.exists()) { InputStream stream = source.getInputStream(); try { properties.load(stream); } catch (IOException e) { throw new RuntimeException(e); } finally { IOUtil.close(stream); } } } String className = ObjectUtil.getSimpleClassName(clazz); StringBuilder propertyName = new StringBuilder(className); if (index > 0) { propertyName.append(".").append(index); } String message = properties.getProperty(propertyName.toString()); if (isEmpty(message)) { message = "!" + clazz.getName() + "!"; } if (params == null) { params = ZERO; } return MessageFormat.format(message, (Object[]) params); } /** * XMLの特殊文字をエスケープして返す。(&, <, >, ") * @param text エスケープ対象の文字列 * @return エスケープ後の文字列 */ public static String escapeXml(String text) { if (text == null) { return ""; } char[] chars = text.toCharArray(); StringBuilder sb = new StringBuilder(chars.length + 50); for (int i = 0; i < chars.length; i++) { switch (chars[i]) { case '&': sb.append("&"); break; case '<': sb.append("<"); break; case '>': sb.append(">"); break; case '"': sb.append("""); break; default: sb.append(chars[i]); } } return sb.toString(); } /** * XMLの特殊文字を"&"を除いてエスケープして返す。(<, >, ") * @param text エスケープ対象の文字列 * @return エスケープ後の文字列 */ public static String escapeXmlWithoutAmp(String text) { if (text == null) { return ""; } char[] chars = text.toCharArray(); StringBuilder sb = new StringBuilder(chars.length + 50); for (int i = 0; i < chars.length; i++) { switch (chars[i]) { case '<': sb.append("<"); break; case '>': sb.append(">"); break; case '"': sb.append("""); break; default: sb.append(chars[i]); } } return sb.toString(); } /** * XMLの空白文字を数値参照にエスケープして返す。(\r, \n, \t) * @param text エスケープ対象の文字列 * @return エスケープ後の文字列 */ public static String escapeWhitespace(String text) { if (text == null) { return ""; } char[] chars = text.toCharArray(); StringBuilder sb = new StringBuilder(chars.length + 50); for (int i = 0; i < chars.length; i++) { switch (chars[i]) { case '\r': sb.append(" "); break; case '\n': sb.append(" "); break; case '\t': sb.append(" "); break; default: sb.append(chars[i]); } } return sb.toString(); } /** * 改行文字を<br>タグにして返す。 * @param text エスケープ対象の文字列 * @param forHTML HTML用か否か(HTML用ならemptyなタグにはしない) * @return エスケープ後の文字列 */ public static String escapeEol(String text, boolean forHTML) { if (text == null) { return ""; } String br = (forHTML ? "
" : "
"); char[] chars = text.toCharArray(); StringBuilder sb = new StringBuilder(chars.length + 50); for (int i = 0; i < chars.length; i++) { switch (chars[i]) { case '\r': sb.append(br); if (chars.length > i + 1 && chars[i + 1] == '\n') { i++; } break; case '\n': sb.append(br); break; default: sb.append(chars[i]); } } return sb.toString(); } /** * プレフィクスがあればプレフィクスを返す。 * @param qName 処理対象の文字列。{@code null}を許容しない。 * @return プレフィクスまたは空文字列。 * @throws IllegalArgumentException {@code qName}が{@code null}の場合に発生する。 */ public static String parsePrefix(String qName) { if (qName == null) { throw new IllegalArgumentException(); } int position = qName.indexOf(':'); if (position >= 0) { return qName.substring(0, position); } return ""; } public static boolean equals(Object s1, Object s2) { return s1 == null && s2 == null || (s1 != null && s1.equals(s2)); } public static String join(Object[] items, String delimiter) { if (items == null) { return String.valueOf(items); } StringBuilder sb = new StringBuilder(); for (int i = 0; i < items.length; i++) { if (i > 0) { sb.append(delimiter); } sb.append(items[i]); } return sb.toString(); } /** * 配列の浅いコピーを作成して返します。 * * @param src 元となる配列 * @return srcの浅いコピー */ public static String[] arraycopy(String[] src) { String[] copy = new String[src.length]; System.arraycopy(src, 0, copy, 0, src.length); return copy; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy