1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xalan.lib;
  58. import org.w3c.dom.*;
  59. import javax.xml.parsers.*;
  60. import org.apache.xpath.NodeSet;
  61. import java.util.StringTokenizer;
  62. /**
  63. * <meta name="usage" content="general"/>
  64. * This class contains EXSLT strings extension functions.
  65. *
  66. * It is accessed by specifying a namespace URI as follows:
  67. * <pre>
  68. * xmlns:math="http://exslt.org/strings"
  69. * </pre>
  70. * The documentation for each function has been copied from the relevant
  71. * EXSLT Implementer page.
  72. *
  73. * @see <a href="http://www.exslt.org/">EXSLT</a>
  74. */
  75. public class ExsltStrings extends ExsltBase
  76. {
  77. // Reuse the Document object to reduce memory usage.
  78. private static Document lDoc = null;
  79. /**
  80. * The str:align function aligns a string within another string.
  81. * <p>
  82. * The first argument gives the target string to be aligned. The second argument gives
  83. * the padding string within which it is to be aligned.
  84. * <p>
  85. * If the target string is shorter than the padding string then a range of characters
  86. * in the padding string are repaced with those in the target string. Which characters
  87. * are replaced depends on the value of the third argument, which gives the type of
  88. * alignment. It can be one of 'left', 'right' or 'center'. If no third argument is
  89. * given or if it is not one of these values, then it defaults to left alignment.
  90. * <p>
  91. * With left alignment, the range of characters replaced by the target string begins
  92. * with the first character in the padding string. With right alignment, the range of
  93. * characters replaced by the target string ends with the last character in the padding
  94. * string. With center alignment, the range of characters replaced by the target string
  95. * is in the middle of the padding string, such that either the number of unreplaced
  96. * characters on either side of the range is the same or there is one less on the left
  97. * than there is on the right.
  98. * <p>
  99. * If the target string is longer than the padding string, then it is truncated to be
  100. * the same length as the padding string and returned.
  101. *
  102. * @param targetStr The target string
  103. * @param paddingStr The padding string
  104. * @param type The type of alignment
  105. *
  106. * @return The string after alignment
  107. */
  108. public static String align(String targetStr, String paddingStr, String type)
  109. {
  110. if (targetStr.length() >= paddingStr.length())
  111. return targetStr.substring(0, paddingStr.length());
  112. if (type.equals("right"))
  113. {
  114. return paddingStr.substring(0, paddingStr.length() - targetStr.length()) + targetStr;
  115. }
  116. else if (type.equals("center"))
  117. {
  118. int startIndex = (paddingStr.length() - targetStr.length()) / 2;
  119. return paddingStr.substring(0, startIndex) + targetStr + paddingStr.substring(startIndex + targetStr.length());
  120. }
  121. // Default is left
  122. else
  123. {
  124. return targetStr + paddingStr.substring(paddingStr.length() - targetStr.length());
  125. }
  126. }
  127. /**
  128. * See above
  129. */
  130. public static String align(String targetStr, String paddingStr)
  131. {
  132. return align(targetStr, paddingStr, "left");
  133. }
  134. /**
  135. * The str:concat function takes a node set and returns the concatenation of the
  136. * string values of the nodes in that node set. If the node set is empty, it returns
  137. * an empty string.
  138. *
  139. * @param nl A node set
  140. * @return The concatenation of the string values of the nodes in that node set
  141. */
  142. public static String concat(NodeList nl)
  143. {
  144. StringBuffer sb = new StringBuffer();
  145. for (int i = 0; i < nl.getLength(); i++)
  146. {
  147. Node node = nl.item(i);
  148. String value = toString(node);
  149. if (value != null && value.length() > 0)
  150. sb.append(value);
  151. }
  152. return sb.toString();
  153. }
  154. /**
  155. * The str:padding function creates a padding string of a certain length.
  156. * The first argument gives the length of the padding string to be created.
  157. * The second argument gives a string to be used to create the padding. This
  158. * string is repeated as many times as is necessary to create a string of the
  159. * length specified by the first argument; if the string is more than a character
  160. * long, it may have to be truncated to produce the required length. If no second
  161. * argument is specified, it defaults to a space (' '). If the second argument is
  162. * an empty string, str:padding returns an empty string.
  163. *
  164. * @param length The length of the padding string to be created
  165. * @param pattern The string to be used as pattern
  166. *
  167. * @return A padding string of the given length
  168. */
  169. public static String padding(double length, String pattern)
  170. {
  171. if (pattern == null || pattern.length() == 0)
  172. return "";
  173. StringBuffer sb = new StringBuffer();
  174. int len = (int)length;
  175. int numAdded = 0;
  176. int index = 0;
  177. while (numAdded < len)
  178. {
  179. if (index == pattern.length())
  180. index = 0;
  181. sb.append(pattern.charAt(index));
  182. index++;
  183. numAdded++;
  184. }
  185. return sb.toString();
  186. }
  187. /**
  188. * See above
  189. */
  190. public static String padding(double length)
  191. {
  192. return padding(length, " ");
  193. }
  194. /**
  195. * The str:split function splits up a string and returns a node set of token
  196. * elements, each containing one token from the string.
  197. * <p>
  198. * The first argument is the string to be split. The second argument is a pattern
  199. * string. The string given by the first argument is split at any occurrence of
  200. * this pattern. For example:
  201. * <pre>
  202. * str:split('a, simple, list', ', ') gives the node set consisting of:
  203. *
  204. * <token>a</token>
  205. * <token>simple</token>
  206. * <token>list</token>
  207. * </pre>
  208. * If the second argument is omitted, the default is the string ' ' (i.e. a space).
  209. *
  210. * @param str The string to be split
  211. * @param pattern The pattern
  212. *
  213. * @return A node set of split tokens
  214. */
  215. public static NodeList split(String str, String pattern)
  216. {
  217. try
  218. {
  219. if (lDoc == null)
  220. lDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
  221. }
  222. catch(ParserConfigurationException pce)
  223. {
  224. throw new org.apache.xml.utils.WrappedRuntimeException(pce);
  225. }
  226. NodeSet resultSet = new NodeSet();
  227. resultSet.setShouldCacheNodes(true);
  228. boolean done = false;
  229. int fromIndex = 0;
  230. int matchIndex = 0;
  231. String token = null;
  232. while (!done && fromIndex < str.length())
  233. {
  234. matchIndex = str.indexOf(pattern, fromIndex);
  235. if (matchIndex >= 0)
  236. {
  237. token = str.substring(fromIndex, matchIndex);
  238. fromIndex = matchIndex + pattern.length();
  239. }
  240. else
  241. {
  242. done = true;
  243. token = str.substring(fromIndex);
  244. }
  245. Element element = lDoc.createElement("token");
  246. Text text = lDoc.createTextNode(token);
  247. element.appendChild(text);
  248. resultSet.addNode(element);
  249. }
  250. return resultSet;
  251. }
  252. /**
  253. * See above
  254. */
  255. public static NodeList split(String str)
  256. {
  257. return split(str, " ");
  258. }
  259. /**
  260. * The str:tokenize function splits up a string and returns a node set of token
  261. * elements, each containing one token from the string.
  262. * <p>
  263. * The first argument is the string to be tokenized. The second argument is a
  264. * string consisting of a number of characters. Each character in this string is
  265. * taken as a delimiting character. The string given by the first argument is split
  266. * at any occurrence of any of these characters. For example:
  267. * <pre>
  268. * str:tokenize('2001-06-03T11:40:23', '-T:') gives the node set consisting of:
  269. *
  270. * <token>2001</token>
  271. * <token>06</token>
  272. * <token>03</token>
  273. * <token>11</token>
  274. * <token>40</token>
  275. * <token>23</token>
  276. * </pre>
  277. * If the second argument is omitted, the default is the string ' '
  278. * (i.e. whitespace characters).
  279. * <p>
  280. * If the second argument is an empty string, the function returns a set of token
  281. * elements, each of which holds a single character.
  282. * <p>
  283. * Note: This one is different from the tokenize extension function in the Xalan
  284. * namespace. The one in Xalan returns a set of Text nodes, while this one wraps
  285. * the Text nodes inside the token Element nodes.
  286. *
  287. * @param toTokenize The string to be tokenized
  288. * @param delims The delimiter string
  289. *
  290. * @return A node set of split token elements
  291. */
  292. public static NodeList tokenize(String toTokenize, String delims)
  293. {
  294. try
  295. {
  296. if (lDoc == null)
  297. lDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
  298. }
  299. catch(ParserConfigurationException pce)
  300. {
  301. throw new org.apache.xml.utils.WrappedRuntimeException(pce);
  302. }
  303. NodeSet resultSet = new NodeSet();
  304. if (delims != null && delims.length() > 0)
  305. {
  306. StringTokenizer lTokenizer = new StringTokenizer(toTokenize, delims);
  307. while (lTokenizer.hasMoreTokens())
  308. {
  309. Element element = lDoc.createElement("token");
  310. element.appendChild(lDoc.createTextNode(lTokenizer.nextToken()));
  311. resultSet.addNode(element);
  312. }
  313. }
  314. // If the delimiter is an empty string, create one token Element for
  315. // every single character.
  316. else
  317. {
  318. for (int i = 0; i < toTokenize.length(); i++)
  319. {
  320. Element element = lDoc.createElement("token");
  321. element.appendChild(lDoc.createTextNode(toTokenize.substring(i, i+1)));
  322. resultSet.addNode(element);
  323. }
  324. }
  325. return resultSet;
  326. }
  327. /**
  328. * See above
  329. */
  330. public static NodeList tokenize(String toTokenize)
  331. {
  332. return tokenize(toTokenize, " \t\n\r");
  333. }
  334. }