1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * $Id: ExsltStrings.java,v 1.15 2004/02/11 17:56:36 minchau Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.lib;
  20. import java.util.StringTokenizer;
  21. import javax.xml.parsers.DocumentBuilderFactory;
  22. import javax.xml.parsers.FactoryConfigurationError;
  23. import javax.xml.parsers.ParserConfigurationException;
  24. import com.sun.org.apache.xpath.internal.NodeSet;
  25. import org.w3c.dom.Document;
  26. import org.w3c.dom.Element;
  27. import org.w3c.dom.Node;
  28. import org.w3c.dom.NodeList;
  29. import org.w3c.dom.Text;
  30. /**
  31. * This class contains EXSLT strings extension functions.
  32. *
  33. * It is accessed by specifying a namespace URI as follows:
  34. * <pre>
  35. * xmlns:str="http://exslt.org/strings"
  36. * </pre>
  37. * The documentation for each function has been copied from the relevant
  38. * EXSLT Implementer page.
  39. *
  40. * @see <a href="http://www.exslt.org/">EXSLT</a>
  41. * @xsl.usage general
  42. */
  43. public class ExsltStrings extends ExsltBase
  44. {
  45. /**
  46. * The str:align function aligns a string within another string.
  47. * <p>
  48. * The first argument gives the target string to be aligned. The second argument gives
  49. * the padding string within which it is to be aligned.
  50. * <p>
  51. * If the target string is shorter than the padding string then a range of characters
  52. * in the padding string are repaced with those in the target string. Which characters
  53. * are replaced depends on the value of the third argument, which gives the type of
  54. * alignment. It can be one of 'left', 'right' or 'center'. If no third argument is
  55. * given or if it is not one of these values, then it defaults to left alignment.
  56. * <p>
  57. * With left alignment, the range of characters replaced by the target string begins
  58. * with the first character in the padding string. With right alignment, the range of
  59. * characters replaced by the target string ends with the last character in the padding
  60. * string. With center alignment, the range of characters replaced by the target string
  61. * is in the middle of the padding string, such that either the number of unreplaced
  62. * characters on either side of the range is the same or there is one less on the left
  63. * than there is on the right.
  64. * <p>
  65. * If the target string is longer than the padding string, then it is truncated to be
  66. * the same length as the padding string and returned.
  67. *
  68. * @param targetStr The target string
  69. * @param paddingStr The padding string
  70. * @param type The type of alignment
  71. *
  72. * @return The string after alignment
  73. */
  74. public static String align(String targetStr, String paddingStr, String type)
  75. {
  76. if (targetStr.length() >= paddingStr.length())
  77. return targetStr.substring(0, paddingStr.length());
  78. if (type.equals("right"))
  79. {
  80. return paddingStr.substring(0, paddingStr.length() - targetStr.length()) + targetStr;
  81. }
  82. else if (type.equals("center"))
  83. {
  84. int startIndex = (paddingStr.length() - targetStr.length()) / 2;
  85. return paddingStr.substring(0, startIndex) + targetStr + paddingStr.substring(startIndex + targetStr.length());
  86. }
  87. // Default is left
  88. else
  89. {
  90. return targetStr + paddingStr.substring(targetStr.length());
  91. }
  92. }
  93. /**
  94. * See above
  95. */
  96. public static String align(String targetStr, String paddingStr)
  97. {
  98. return align(targetStr, paddingStr, "left");
  99. }
  100. /**
  101. * The str:concat function takes a node set and returns the concatenation of the
  102. * string values of the nodes in that node set. If the node set is empty, it returns
  103. * an empty string.
  104. *
  105. * @param nl A node set
  106. * @return The concatenation of the string values of the nodes in that node set
  107. */
  108. public static String concat(NodeList nl)
  109. {
  110. StringBuffer sb = new StringBuffer();
  111. for (int i = 0; i < nl.getLength(); i++)
  112. {
  113. Node node = nl.item(i);
  114. String value = toString(node);
  115. if (value != null && value.length() > 0)
  116. sb.append(value);
  117. }
  118. return sb.toString();
  119. }
  120. /**
  121. * The str:padding function creates a padding string of a certain length.
  122. * The first argument gives the length of the padding string to be created.
  123. * The second argument gives a string to be used to create the padding. This
  124. * string is repeated as many times as is necessary to create a string of the
  125. * length specified by the first argument; if the string is more than a character
  126. * long, it may have to be truncated to produce the required length. If no second
  127. * argument is specified, it defaults to a space (' '). If the second argument is
  128. * an empty string, str:padding returns an empty string.
  129. *
  130. * @param length The length of the padding string to be created
  131. * @param pattern The string to be used as pattern
  132. *
  133. * @return A padding string of the given length
  134. */
  135. public static String padding(double length, String pattern)
  136. {
  137. if (pattern == null || pattern.length() == 0)
  138. return "";
  139. StringBuffer sb = new StringBuffer();
  140. int len = (int)length;
  141. int numAdded = 0;
  142. int index = 0;
  143. while (numAdded < len)
  144. {
  145. if (index == pattern.length())
  146. index = 0;
  147. sb.append(pattern.charAt(index));
  148. index++;
  149. numAdded++;
  150. }
  151. return sb.toString();
  152. }
  153. /**
  154. * See above
  155. */
  156. public static String padding(double length)
  157. {
  158. return padding(length, " ");
  159. }
  160. /**
  161. * The str:split function splits up a string and returns a node set of token
  162. * elements, each containing one token from the string.
  163. * <p>
  164. * The first argument is the string to be split. The second argument is a pattern
  165. * string. The string given by the first argument is split at any occurrence of
  166. * this pattern. For example:
  167. * <pre>
  168. * str:split('a, simple, list', ', ') gives the node set consisting of:
  169. *
  170. * <token>a</token>
  171. * <token>simple</token>
  172. * <token>list</token>
  173. * </pre>
  174. * If the second argument is omitted, the default is the string ' ' (i.e. a space).
  175. *
  176. * @param str The string to be split
  177. * @param pattern The pattern
  178. *
  179. * @return A node set of split tokens
  180. */
  181. public static NodeList split(String str, String pattern)
  182. {
  183. NodeSet resultSet = new NodeSet();
  184. resultSet.setShouldCacheNodes(true);
  185. boolean done = false;
  186. int fromIndex = 0;
  187. int matchIndex = 0;
  188. String token = null;
  189. while (!done && fromIndex < str.length())
  190. {
  191. matchIndex = str.indexOf(pattern, fromIndex);
  192. if (matchIndex >= 0)
  193. {
  194. token = str.substring(fromIndex, matchIndex);
  195. fromIndex = matchIndex + pattern.length();
  196. }
  197. else
  198. {
  199. done = true;
  200. token = str.substring(fromIndex);
  201. }
  202. Document doc = DocumentHolder.m_doc;
  203. synchronized (doc)
  204. {
  205. Element element = doc.createElement("token");
  206. Text text = doc.createTextNode(token);
  207. element.appendChild(text);
  208. resultSet.addNode(element);
  209. }
  210. }
  211. return resultSet;
  212. }
  213. /**
  214. * See above
  215. */
  216. public static NodeList split(String str)
  217. {
  218. return split(str, " ");
  219. }
  220. /**
  221. * The str:tokenize function splits up a string and returns a node set of token
  222. * elements, each containing one token from the string.
  223. * <p>
  224. * The first argument is the string to be tokenized. The second argument is a
  225. * string consisting of a number of characters. Each character in this string is
  226. * taken as a delimiting character. The string given by the first argument is split
  227. * at any occurrence of any of these characters. For example:
  228. * <pre>
  229. * str:tokenize('2001-06-03T11:40:23', '-T:') gives the node set consisting of:
  230. *
  231. * <token>2001</token>
  232. * <token>06</token>
  233. * <token>03</token>
  234. * <token>11</token>
  235. * <token>40</token>
  236. * <token>23</token>
  237. * </pre>
  238. * If the second argument is omitted, the default is the string ' '
  239. * (i.e. whitespace characters).
  240. * <p>
  241. * If the second argument is an empty string, the function returns a set of token
  242. * elements, each of which holds a single character.
  243. * <p>
  244. * Note: This one is different from the tokenize extension function in the Xalan
  245. * namespace. The one in Xalan returns a set of Text nodes, while this one wraps
  246. * the Text nodes inside the token Element nodes.
  247. *
  248. * @param toTokenize The string to be tokenized
  249. * @param delims The delimiter string
  250. *
  251. * @return A node set of split token elements
  252. */
  253. public static NodeList tokenize(String toTokenize, String delims)
  254. {
  255. NodeSet resultSet = new NodeSet();
  256. if (delims != null && delims.length() > 0)
  257. {
  258. StringTokenizer lTokenizer = new StringTokenizer(toTokenize, delims);
  259. Document doc = DocumentHolder.m_doc;
  260. synchronized (doc)
  261. {
  262. while (lTokenizer.hasMoreTokens())
  263. {
  264. Element element = doc.createElement("token");
  265. element.appendChild(doc.createTextNode(lTokenizer.nextToken()));
  266. resultSet.addNode(element);
  267. }
  268. }
  269. }
  270. // If the delimiter is an empty string, create one token Element for
  271. // every single character.
  272. else
  273. {
  274. Document doc = DocumentHolder.m_doc;
  275. synchronized (doc)
  276. {
  277. for (int i = 0; i < toTokenize.length(); i++)
  278. {
  279. Element element = doc.createElement("token");
  280. element.appendChild(doc.createTextNode(toTokenize.substring(i, i+1)));
  281. resultSet.addNode(element);
  282. }
  283. }
  284. }
  285. return resultSet;
  286. }
  287. /**
  288. * See above
  289. */
  290. public static NodeList tokenize(String toTokenize)
  291. {
  292. return tokenize(toTokenize, " \t\n\r");
  293. }
  294. /**
  295. * This class is not loaded until first referenced (see Java Language
  296. * Specification by Gosling/Joy/Steele, section 12.4.1)
  297. *
  298. * The static members are created when this class is first referenced, as a
  299. * lazy initialization not needing checking against null or any
  300. * synchronization.
  301. *
  302. */
  303. private static class DocumentHolder
  304. {
  305. // Reuse the Document object to reduce memory usage.
  306. private static final Document m_doc;
  307. static {
  308. try
  309. {
  310. m_doc =DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
  311. }
  312. catch(ParserConfigurationException pce)
  313. {
  314. throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(pce);
  315. }
  316. }
  317. }
  318. }