blob: 140ffcb295fd2af67c15b7bfb85dcbc00646564d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2010 Andrea Bittau, University College London, and others
* 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:
* Andrea Bittau - initial API and implementation from the PsychoPath XPath 2.0
* Jesper Steen Moeller - bug 282096 - clean up string storage and make
* translate function surrogate aware
* Mukul Gandhi - bug 280798 - PsychoPath support for JDK 1.4
*******************************************************************************/
package org.eclipse.wst.xml.xpath2.processor.internal.function;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.wst.xml.xpath2.api.ResultSequence;
import org.eclipse.wst.xml.xpath2.processor.DynamicError;
import org.eclipse.wst.xml.xpath2.processor.internal.SeqType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.QName;
import org.eclipse.wst.xml.xpath2.processor.internal.types.XSString;
import org.eclipse.wst.xml.xpath2.processor.internal.utils.CodePointIterator;
import org.eclipse.wst.xml.xpath2.processor.internal.utils.StringCodePointIterator;
import com.ibm.icu.lang.UCharacter;
/**
* <p>
* Translation function.
* </p>
*
* <p>
* Usage: fn:translate($arg as xs:string?, $mapString as xs:string, $transString
* as xs:string) as xs:string
* </p>
*
* <p>
* This class returns the value of $arg modified so that every character in the
* value of $arg that occurs at some position N in the value of $mapString has
* been replaced by the character that occurs at position N in the value of
* $transString.
* </p>
*
* <p>
* If the value of $arg is the empty sequence, the zero-length string is
* returned.
* </p>
*
* <p>
* Every character in the value of $arg that does not appear in the value of
* $mapString is unchanged.
* </p>
*
* <p>
* Every character in the value of $arg that appears at some position M in the
* value of $mapString, where the value of $transString is less than M
* characters in length, is omitted from the returned value. If $mapString is
* the zero-length string $arg is returned.
* </p>
*
* <p>
* If a character occurs more than once in $mapString, then the first occurrence
* determines the replacement character. If $transString is longer than
* $mapString, the excess characters are ignored.
* </p>
*/
public class FnTranslate extends Function {
private static Collection _expected_args = null;
/**
* Constructor for FnTranslate.
*/
public FnTranslate() {
super(new QName("translate"), 3);
}
/**
* Evaluate the arguments.
*
* @param args
* are evaluated.
* @throws DynamicError
* Dynamic error.
* @return The evaluation of the arguments being translated.
*/
public ResultSequence evaluate(Collection args, org.eclipse.wst.xml.xpath2.api.EvaluationContext ec) throws DynamicError {
return translate(args);
}
/**
* Translate arguments.
*
* @param args
* are translated.
* @throws DynamicError
* Dynamic error.
* @return The result of translating the arguments.
*/
public static ResultSequence translate(Collection args) throws DynamicError {
Collection cargs = Function.convert_arguments(args, expected_args());
Iterator argi = cargs.iterator();
ResultSequence arg1 = (ResultSequence) argi.next();
ResultSequence arg2 = (ResultSequence) argi.next();
ResultSequence arg3 = (ResultSequence) argi.next();
if (arg1.empty()) {
return new XSString("");
}
String str = ((XSString) arg1.first()).value();
String mapstr = ((XSString) arg2.first()).value();
String transstr = ((XSString) arg3.first()).value();
Map replacements = buildReplacementMap(mapstr, transstr);
StringBuffer sb = new StringBuffer(str.length());
CodePointIterator strIter = new StringCodePointIterator(str);
for (int input = strIter.current(); input != CodePointIterator.DONE; input = strIter.next()) {
Integer inputCodepoint = new Integer(input);
if (replacements.containsKey(inputCodepoint)) {
Integer replaceWith = (Integer)replacements.get(inputCodepoint);
if (replaceWith != null) {
sb.append(UCharacter.toChars(replaceWith.intValue()));
}
} else {
sb.append(UCharacter.toChars(input));
}
}
return new XSString(sb.toString());
}
/**
* Build a replacement map from the mapstr and the transstr for translation. The function returns a Map<Integer, Integer> mapping each codepoint
* mentioned in the mapstr into the corresponding codepoint in transstr, or null if there is no matching mapping in transstr.
*
* @param mapstr The "mapping from" string
* @param transstr The "mapping into" string
* @return A map which maps input codepoint to output codepoint (or null)
*/
private static Map buildReplacementMap(String mapstr, String transstr) {
// Build mapping (map from codepoint -> codepoint)
Map replacements = new HashMap(mapstr.length() * 4);
CodePointIterator mapIter = new StringCodePointIterator(mapstr);
CodePointIterator transIter = new StringCodePointIterator(transstr);
// Iterate through both mapIter and transIter and produce the mapping
int mapFrom = mapIter.current();
int mapTo = transIter.current();
while (mapFrom != CodePointIterator.DONE) {
Integer codepointFrom = new Integer(mapFrom);
if (! replacements.containsKey(codepointFrom)) {
// only overwrite if it doesn't exist already
Integer replacement = mapTo != CodePointIterator.DONE ? new Integer(mapTo) : null;
replacements.put(codepointFrom, replacement);
}
mapFrom = mapIter.next();
mapTo = transIter.next();
}
return replacements;
}
/**
* Calculate the expected arguments.
*
* @return The expected arguments.
*/
public synchronized static Collection expected_args() {
if (_expected_args == null) {
_expected_args = new ArrayList();
_expected_args.add(new SeqType(new XSString(), SeqType.OCC_QMARK));
_expected_args.add(new SeqType(new XSString(), SeqType.OCC_NONE));
_expected_args.add(new SeqType(new XSString(), SeqType.OCC_NONE));
}
return _expected_args;
}
}