package org.xmlsh.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLEncoder; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.PathMatcher; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.EnumSet; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import net.sf.saxon.event.ComplexContentOutputter; import net.sf.saxon.event.NamespaceReducer; import net.sf.saxon.event.Receiver; import net.sf.saxon.event.TreeReceiver; import net.sf.saxon.lib.FeatureKeys; import net.sf.saxon.om.Item; import net.sf.saxon.om.NodeInfo; import net.sf.saxon.s9api.Destination; import net.sf.saxon.s9api.SaxonApiException; import net.sf.saxon.s9api.Serializer; import net.sf.saxon.s9api.XdmAtomicValue; import net.sf.saxon.s9api.XdmItem; import net.sf.saxon.s9api.XdmNode; import net.sf.saxon.s9api.XdmNodeKind; import net.sf.saxon.s9api.XdmValue; import net.sf.saxon.trans.XPathException; import org.apache.logging.log4j.Logger; import org.xmlsh.core.CoreException; import org.xmlsh.core.IReleasable; import org.xmlsh.core.Namespaces; import org.xmlsh.core.UnexpectedException; import org.xmlsh.core.XValue; import org.xmlsh.sh.core.CharAttributeBuffer; import org.xmlsh.sh.core.CharAttrs; import org.xmlsh.sh.shell.SerializeOpts; import org.xmlsh.sh.shell.Shell; import org.xmlsh.sh.shell.ShellConstants; import org.xmlsh.types.ITypeConverter; /** * @author DLEE * * Utility Functions */ public class Util { private static final String sXSDT_FORMAT_STR = "yyyy-MM-dd'T'HH:mm:ss"; public static volatile byte mNewLineBytes[]; private static Pattern mURIPattern = Pattern .compile("[a-zA-Z][a-zA-Z0-9\\.+-]+:.*"); private static volatile String mNewlineString; private static Logger mLogger = org.apache.logging.log4j.LogManager .getLogger(Util.class); public static final char kESCAPE = '\\'; private static class FileComparator implements Comparator<File> { @Override public int compare(File o1, File o2) { return FileUtils.toJavaPath(o1.getName()) .compareTo(FileUtils.toJavaPath(o2.getName())); } } public static boolean isEmpty(String s) { return s == null || s.length() == 0; } public static boolean isBlank(String s) { if(s == null) return true; return isEmpty(s.trim()); } /* * Copy an input to an output stream */ public static long copyStream(InputStream is, OutputStream os) throws IOException { byte[] buf = new byte[1024]; int len; long size = 0; while((len = is.read(buf)) > 0) { os.write(buf, 0, len); size += len; } return size; } /* * Read in input stream into a string */ public static byte[] readBytes(InputStream is) throws IOException { ByteArrayOutputStream bs = new ByteArrayOutputStream(); @SuppressWarnings("unused") long size = copyStream(is, bs); return bs.toByteArray(); } public static String readString(InputStream is, String encoding) throws IOException { return new String(readBytes(is), encoding); } public static String readString(URL url, String encoding) throws IOException { InputStream is = url.openStream(); String ret = new String(readBytes(is), encoding); is.close(); return ret; } public static String readString(File file, String encoding) throws IOException { FileInputStream fis = new FileInputStream(file); String ret = readString(fis, encoding); fis.close(); return ret; } public static String replace(String str, String pattern, String replace) { if(replace == null) { replace = ""; //$NON-NLS-1$ } int s = 0, e = 0; StringBuffer result = new StringBuffer(); while((e = str.indexOf(pattern, s)) >= 0) { result.append(str.substring(s, e)); result.append(replace); s = e + pattern.length(); } if(s == 0) return str; result.append(str.substring(s)); return result.toString(); } public static boolean isInt(String string, boolean sign) { for(int i = 0; i < string.length(); i++) { char c = string.charAt(i); if(sign && i == 0) { if(c == '+' || c == '-') continue; } if(!Character.isDigit(c)) return false; } return true; } /** * Method intValue. * * @param string * @return int */ public static int parseInt(String string, int defValue) { if(isEmpty(string)) return defValue; try { return Integer.parseInt(string); } catch (NumberFormatException e) { return defValue; } } public static long parseLong(String string, long defValue) { if(isEmpty(string)) return defValue; try { return Long.parseLong(string); } catch (NumberFormatException e) { return defValue; } } /** * Method trim. * * @param string * @return String */ public static String trim(String string) { return string == null ? "" : string.trim(); //$NON-NLS-1$ } public static String repeat(char c, int n) { StringBuffer sb = new StringBuffer(n); while(n-- > 0) sb.append(c); return sb.toString(); } public static String pad(String str, int width, char pad, boolean bRight) { if(str == null) str = ""; //$NON-NLS-1$ int len = str.length(); // Blank pad to mWidth if(len < width) { if(bRight) str = repeat(pad, width - len) + str; else str = str + repeat(pad, width - len); } return str; } public static String pad(String str, int width) { return pad(str, width, ' ', false); } public static String pad(int width) { return pad(null, width, ' ', false); } public static String lineBreak(boolean bHtml) { return bHtml ? "<br>\n" : "\n"; //$NON-NLS-1$ //$NON-NLS-2$ } public static boolean isTrue(String sEnabled) { if(sEnabled == null) return false; return sEnabled.equalsIgnoreCase("true") //$NON-NLS-1$ || sEnabled.equalsIgnoreCase("yes"); //$NON-NLS-1$ } /** * Method parseDouble. * * @param string * @return double */ public static double parseDouble(String str, double defValue) { try { return Double.valueOf(str).doubleValue(); } catch (Exception e) { mLogger.info("Exception parsing double: " + str, e); } return defValue; } /** * Method parseDouble. * * @param string * @return double */ public static double parseDouble(String str) { return parseDouble(str, 0.); } public static boolean parseBoolean(String string) { return parseBoolean(string, false); } public static boolean parseBoolean(String string, boolean def) { if(isBlank(string)) return def; if("1".equals(string) || "true".equals(string)) return true; return def; } public static String urlEncode(String value) { try { return URLEncoder.encode(value, ShellConstants.kENCODING_UTF_8); } catch (UnsupportedEncodingException e) { mLogger.info("Exception encoding URL to UTF-8: " + value, e); return value; } } /** * String equality without crashing if null * * @param string * @param string2 * @return */ public static boolean isEqual(String string1, String string2) { return isEqual(string1, string2, false); } public static boolean isEqual(String string1, String string2, boolean ignCase) { // DAL: 2010-11-10 Optimize out multiple calls to isEmpty boolean bIsEmpty1 = (string1 == null || string1.length() == 0); boolean bIsEmpty2 = (string2 == null || string2.length() == 0); // both null/"" true if(bIsEmpty1 && bIsEmpty2) return true; // either null/"" but not both = false if(bIsEmpty1 || bIsEmpty2) return false; return ignCase ? string1.equalsIgnoreCase(string2) : string1.equals(string2); } /** * @param message * @param i * @return */ public static String trim(String message, int i) { message = trim(message); if(message.length() > i) message = message.substring(0, i); return message; } public static String toHex(byte b) { String hex = Integer.toString(b & 0xFF, 16); if(hex.length() < 2) hex = "0" + hex; return hex; } public static String notNull(String str) { if(str == null) return ""; return str; } public static boolean notEmpty(String str) { return !isEmpty(str); } public static boolean notBlank(String str) { return !isBlank(str); } public static String nullIfBlank(String str) { return isBlank(str) ? null : str.trim(); } public static String formatMessage(Exception e) { String msg = e.getMessage(); Throwable cause = e.getCause(); if(cause != null) msg = msg + "\nCause: " + cause.getMessage(); return msg; } public static TransformerHandler getTransformerHander(OutputStream stdout, SerializeOpts opts) throws TransformerFactoryConfigurationError, TransformerConfigurationException, IllegalArgumentException, IOException { return getTransformerHander(new StreamResult(stdout), opts); } public static TransformerHandler getTransformerHander(Result result, SerializeOpts opts) throws TransformerFactoryConfigurationError, TransformerConfigurationException, IllegalArgumentException, IOException { SAXTransformerFactory tf = (SAXTransformerFactory) TransformerFactory .newInstance(); tf.setAttribute(FeatureKeys.CONFIGURATION, Shell.getProcessor().getUnderlyingConfiguration()); // SAX2.0 ContentHandler. TransformerHandler hd = tf.newTransformerHandler(); Transformer serializer = hd.getTransformer(); serializer.setOutputProperty(OutputKeys.ENCODING, opts.getOutputXmlEncoding()); // serializer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,"users.dtd"); serializer.setOutputProperty(OutputKeys.INDENT, opts.isIndent() ? "yes" : "no"); serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, opts.isOmit_xml_declaration() ? "yes" : "no"); hd.setResult(result); return hd; } public static boolean isIdentifier(char c) { return c == '_' || Character.isLetter(c) || Character.isDigit(c); } /* * Wrapper over NIO PathMatcher */ public static PathMatcher getPathMatcher(String pattern) { try { return FileSystems.getDefault().getPathMatcher("glob:" + pattern); } catch (IllegalArgumentException | UnsupportedOperationException e) { return null; } } /* * Filename style wildcard matching - ONLY for non filenames * * Use getPathMatcher instead for real files - or maybe not ... */ public static boolean wildMatches(String pattern, String word, boolean caseSensitive) { /* * Create a java regex that coresponds to pattern */ return compileWild(pattern, caseSensitive).matcher(word).matches(); } public static boolean containsWild(CharAttributeBuffer pattern) { int inbrack = 0; byte[] attrs = pattern.getAttrArray(); char[] chars = pattern.getCharArray(); // ANY of the char attrs prevents wild expansion for(int i = 0; i < chars.length; i++) { if(attrs[i] != 0) { continue; } switch(chars[i]){ case '*': case '?': return true; case '[': inbrack++; break; case ']': if(inbrack > 0) return true; } } return false; } public static Pattern compileWild(CharAttributeBuffer pattern, boolean caseSensitive) { StringBuilder sb = new StringBuilder(); StringBuilder literal = new StringBuilder(); byte[] attrs = pattern.getAttrArray(); char[] chars = pattern.getCharArray(); byte escape = CharAttr.ATTR_ESCAPED.toBit(); for(int i = 0; i < chars.length; i++) { if((attrs[i] & escape) == escape) literal.append('\\'); // ANY of the char attrs prevents wild expansion if(attrs[i] != 0) { literal.append(chars[i]); continue; } String wild = null; switch(chars[i]){ case '*': wild = ".*"; break; case '?': wild = "."; break; case '[': int p = i + 1; while(p < chars.length && chars[p] != ']') { p++; } if(p < chars.length) { wild = pattern.subsequence(i, p + 1).toString().toString(); i = p; break; } // Fall through default: literal.append(chars[i]); continue; } if(literal.length() > 0) { sb.append(Pattern.quote(literal.toString())); literal.setLength(0); } if(wild != null) sb.append(wild); } if(literal.length() > 0) sb.append(Pattern.quote(literal.toString())); return Pattern.compile(sb.toString(), caseSensitive ? 0 : Pattern.CASE_INSENSITIVE); } public static Pattern compileWild(String pattern, boolean caseSensitive) { String reg = "^" + pattern.replace("^", "\\^").replace("+", "\\+").replace(".", "\\.") .replace("*", ".*").replace("?", ".").replace("(", "\\(") .replace(")", "\\)").replace("}", "\\}").replace("{", "/{") + "$"; // Special case, single "[" (test) if(reg.equals("^[$")) reg = "^\\[$"; return Pattern.compile(reg, caseSensitive ? 0 : Pattern.CASE_INSENSITIVE); } public static PathMatcher compileWild( FileSystem fileSystem, CharAttributeBuffer wild, CharAttrs escapeAttrs, boolean caseSensitive) { String wildstr = wild.decodeString(); try { return fileSystem.getPathMatcher("glob:" + wildstr); } catch (PatternSyntaxException e) { mLogger.trace("Invalid glob expansion: {}", wildstr, e); } return null; } public static boolean hasAnyChar(String s, String any) { for(int i = 0; i < any.length(); i++) { char c = any.charAt(i); if(s.indexOf(c) >= 0) return true; } return false; } public static int parseInt(XValue value, int def) { return parseInt(value.toString(), def); } public static List<XValue> toList(XValue[] args) { ArrayList<XValue> a = new ArrayList<XValue>(); for(int i = 0; i < args.length; i++) a.add(args[i]); return a; } public static List<String> toList(String[] args) { ArrayList<String> a = new ArrayList<String>(); for(int i = 0; i < args.length; i++) a.add(args[i]); return a; } public static List<String> toStringList(List<XValue> a) { ArrayList<String> list = new ArrayList<String>(); for(XValue v : a) list.add(v.toString()); return list; } public static String[] toStringArray(List<XValue> a) { List<String> list = toStringList(a); return list.toArray(new String[list.size()]); } public static String readLine(InputStream is, String encoding) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); int c; boolean bAny = false; while((c = is.read()) > 0 && c != '\n') { bAny = true; if(c != '\r') bos.write(c); } if(c == -1 && !bAny) return null; bos.close(); return bos.toString(encoding); } public static List<XValue> expandSequences(List<XValue> values) { /* * Avoid making a new list if this list is a single element */ if(values == null || values.isEmpty()) return values; ArrayList<XValue> list = new ArrayList<XValue>(values.size()); for(XValue arg : values) { if(arg == null) continue; for(XValue v : arg) list.add(v); } return list; } public static byte[] getNewlineBytes(SerializeOpts opts) { if(Util.mNewLineBytes == null) { try { Util.mNewLineBytes = System.getProperty("line.separator") .getBytes(opts.getOutputTextEncoding()); } catch (UnsupportedEncodingException e) { if(isWindows()) Util.mNewLineBytes = new byte[] { '\r', '\n' }; else Util.mNewLineBytes = new byte[] { '\n' }; } } return Util.mNewLineBytes; } public static String getNewlineString() { if(Util.mNewlineString == null) Util.mNewlineString = System.getProperty("line.separator"); if(Util.mNewlineString == null) { if(isWindows()) Util.mNewlineString = "\r\n"; else Util.mNewlineString = "\n"; } return Util.mNewlineString; } public static void sortFiles(File[] list) { Arrays.sort(list, new FileComparator()); } public static void sortFiles(List<File> list) { Collections.sort(list, new FileComparator()); } public static String readLine(Reader ir) throws IOException { StringBuffer sb = new StringBuffer(); int c; boolean bAny = false; while((c = ir.read()) > 0 && c != '\n') { bAny = true; if(c != '\r') sb.append((char) c); } if(c == -1 && !bAny) return null; return sb.toString(); } public static InputStream toInputStream(String script, SerializeOpts opts) throws UnsupportedEncodingException { return new ByteArrayInputStream( script.getBytes(opts.getInputTextEncoding())); } public static void writeXdmValue(XdmValue value, Destination destination) throws SaxonApiException, IOException { for(Iterator<XdmItem> it = value.iterator(); it.hasNext();) { XdmItem item = it.next(); writeXdmItem(item, destination); } } public static void writeXdmItem(XdmItem item, Destination destination) throws SaxonApiException, IOException { try { if(item instanceof XdmNode) { XdmNode node = (XdmNode) item; if(node.getNodeKind() == XdmNodeKind.ATTRIBUTE) item = new XdmAtomicValue(node.getStringValue()); } Receiver out = destination .getReceiver(Shell.getProcessor().getUnderlyingConfiguration()); // DAL: 2010-10-15 - // Added in namespace reducer in order to filter out duplicate and // redundant namespaces out = new NamespaceReducer(out); ComplexContentOutputter out2 = new ComplexContentOutputter( Shell.getProcessor().getUnderlyingConfiguration() .makePipelineConfiguration()); out2.setReceiver(out); TreeReceiver tree = new TreeReceiver(out2); tree.open(); tree.startDocument(0); tree.append((Item) item.getUnderlyingValue(), 0, NodeInfo.LOCAL_NAMESPACES); // NodeInfo.NO_NAMESPACES);//NodeInfo.ALL_NAMESPACES); tree.endDocument(); tree.close(); } catch (XPathException err) { throw new SaxonApiException(err); } } /* * Try to create a URI from a string and return null instead of exception on * failure * */ public static URI tryURI(String s) { // First check for a-z{2}+ URI uri = null; Matcher m = mURIPattern.matcher(s); if(m.matches()) try { uri = new URI(s); } catch (URISyntaxException e) { // Really ignore this return null; } return uri; } public static URL tryURL(String s) { URI uri = tryURI(s); if(uri != null) try { return uri.toURL(); } catch (MalformedURLException e) { return null; } return null; } /* * * public static boolean isURIScheme(String file) { * * return file.startsWith("http:") || * file.startsWith("https:") || * file.startsWith("ftp:") || * file.startsWith("file:") || * file.startsWith("jar:"); * * * return file.matches("[a-z][a-z]+:.*"); * } * */ public static String readString(URI uri, String encoding) throws MalformedURLException, IOException { return readString(uri.toURL(), encoding); } public static Destination streamToDestination(OutputStream out, SerializeOpts opts) throws IOException { Serializer dest = getSerializer(opts); dest.setOutputStream(out); return dest; } public static Serializer getSerializer(SerializeOpts opts) throws IOException { Serializer ser = Shell.getProcessor().newSerializer(); ser.setOutputProperty(Serializer.Property.OMIT_XML_DECLARATION, opts.isOmit_xml_declaration() ? "yes" : "no"); ser.setOutputProperty(Serializer.Property.INDENT, opts.isIndent() ? "yes" : "no"); // dest.setOutputProperty(Serializer.Property.VERSION,"1.1"); ser.setOutputProperty(Serializer.Property.METHOD, opts.getMethod()); ser.setOutputProperty(Serializer.Property.ENCODING, opts.getOutputXmlEncoding()); return ser; } public static boolean isWindows() { return System.getProperty("os.name").startsWith("Windows"); } public static boolean isOSX() { String osName = System.getProperty("os.name"); return osName.contains("OS X"); } public static XdmNode asXdmNode(URL url) throws IOException, SaxonApiException { InputStream is = url.openStream(); try { Source s = new StreamSource(is); s.setSystemId(url.toExternalForm()); net.sf.saxon.s9api.DocumentBuilder builder = Shell.getProcessor() .newDocumentBuilder(); return builder.build(s); } finally { is.close(); } } // Format as xs:datetime public static String formatXSDateTime(Date date) { // YYYY-MM-DDThh:mm:ss if(date == null) return ""; // @TODO: zero date ? return (new SimpleDateFormat(sXSDT_FORMAT_STR)).format(date); } public static void safeClose(AutoCloseable closable) { try { if(closable != null) closable.close(); } catch (Exception e) { mLogger.info("Exception closing: " + closable.getClass().getName(), e); } } public static void safeClose(XMLStreamWriter out) { try { if(out != null) out.close(); } catch (Exception e) { mLogger.debug("Exception closing XMLStreamWriter", e); } } public static void safeClose(XMLStreamReader reader) { try { if(reader != null) reader.close(); } catch (Exception e) { mLogger.debug("Exception closing XMLStreamReader", e); } } /** * DAL: NOTE: Fixed version of the Saxon S9API function of the same name in * QName * Saxon version truncates the namespaceURI by 1 letter * * Factory method to construct a QName from a string containing the expanded * QName in Clark notation, that is, <code>{uri}local</code> * <p/> * The prefix part of the <code>QName</code> will be set to an empty string. * </p> * * @param expandedName * The URI in Clark notation: <code>{uri}local</code> if the * name is in a namespace, or simply <code>local</code> if not. * @return the QName corresponding to the supplied name in Clark notation. * This will always * have an empty prefix. */ public static QName qnameFromClarkName(String expandedName) { String namespaceURI; String localName; if(expandedName.charAt(0) == '{') { int closeBrace = expandedName.indexOf('}'); if(closeBrace < 0) { throw new IllegalArgumentException("No closing '}' in Clark name"); } namespaceURI = expandedName.substring(1, closeBrace /* * DAL: FIX: WAS: -1 */ ); if(closeBrace == expandedName.length()) { throw new IllegalArgumentException("Missing local part in Clark name"); } localName = expandedName.substring(closeBrace + 1); } else { namespaceURI = ""; localName = expandedName; } return new QName(namespaceURI, localName, ""); } /* * Resolve a string as a QName * May be * local * prefix:local * {clarknotation}local * * Only if prefix:is used is the ns used * */ public static QName resolveQName(String name, Namespaces ns) { if(name.startsWith("{")) return qnameFromClarkName(name); int colon = name.indexOf(':'); if(colon < 0) return new QName(name); String prefix = name.substring(0, colon); String local = name.substring(colon + 1); String uri = ""; if(prefix.length() > 0) uri = ns.get(prefix); if(uri == null) uri = ""; return new QName(uri, local, prefix); } private static void copyFile(File src, File dest, boolean force) throws IOException { // Try copy InputStream in = null; OutputStream out = null; try { in = new FileInputStream(src); // Try deleting dest if we have to if(force && dest.exists() && !dest.canWrite()) dest.delete(); out = new FileOutputStream(dest); Util.copyStream(in, out); } finally { if(in != null) in.close(); if(out != null) out.close(); } } /** * Move a file, possibly renaming it * * @param inFile * @param file */ public static void moveFile(File src, File dest, boolean force) throws IOException { if(dest.exists() && force) dest.delete(); // Simple rename if(src.renameTo(dest)) return; copyFile(src, dest, force); src.delete(); } /* * Return true if variable is PATH or XPATH * On Windows env variables are case InSensitive * */ public static boolean isPath(String var) { if(isWindows()) return var.equalsIgnoreCase(ShellConstants.PATH) || var.equalsIgnoreCase(ShellConstants.ENV_XPATH); else return var.equals(ShellConstants.PATH) || var.equals(ShellConstants.ENV_XPATH); } /* * Convert a List of XValues into a List with 1 XValue */ public static List<XValue> combineSequence(List<XValue> result) { if(result == null) return XValue.emptyList(); if(result.size() < 2) return result; XValue value = XValue.newXValue(result); List<XValue> v = new ArrayList<XValue>(1); v.add(value); return v; } // Format time as xs:datetime public static String formatXSDateTime(long lastModified) { Date date = new Date(lastModified); return formatXSDateTime(date); } public static List<XValue> toXValueList(String[] args) { List<XValue> list = new ArrayList<XValue>(args.length); for(String a : args) list.add(XValue.newXValue(a)); return list; } private static int fromHexChar(char c) { if(c >= 'A' && c <= 'F') return (c - 'A'); if(c >= '0' && c <= '9') return (c - '0'); return -1; } public static int fromHexChars(char[] chars, int i) { if(chars.length < i + 2) return -1; int n1 = fromHexChar(chars[i]); if(n1 < 0) return -1; int n2 = fromHexChar(chars[i + 1]); if(n2 < 0) return -1; byte b = (byte) ((n1 << 4) | n2); return b & 0xFF; } public static void toHexByte(byte b, StringBuffer sb) { int n1 = (b & 0xF0) >> 4; int n2 = (b & 0xF); sb.append((char) (n1 < 10 ? n1 + '0' : (n1 - 10) + 'A')); sb.append((char) (n2 < 10 ? n2 + '0' : (n2 - 10) + 'A')); } public static String toHex(byte[] data) { StringBuffer sb = new StringBuffer(data.length * 2); for(byte b : data) toHexByte(b, sb); return sb.toString(); } public static void toHexChar(char ch, StringBuffer sb) { if((ch & 0xFF00) != 0) toHexByte((byte) ((ch >> 8) & 0xFF), sb); toHexByte((byte) (ch & 0xFF), sb); } public static QName encodeForQNameSimple(String name) { return new QName(encodeForNCNameSimple(name)); } public static QName encodeForQName(String name) { if(!name.contains("::")) return new QName(encodeForNCName(name)); String ns[] = name.split("::"); // TODO: Lookup actual namespace return new QName(encodeForNCName(ns[0]), encodeForNCName(ns[1]), encodeForNCName(ns[0])); } public static String[] split( String s, char cs) { return split(s,cs,kESCAPE ); } public static String[] split( String s , char cs , char esc ) { return CharAttributeBuffer.encodeString(s,esc).splitString(cs , esc, false ); } // Simplified compatible versions of xdmp:encode-for-NCName and // xdmp:decode-from-NCName public static String encodeForNCName(String name) { StringBuffer sb = new StringBuffer(name.length() * 2); char[] chars = name.toCharArray(); boolean bFirst = true; boolean escaped = false; for(char ch : chars) { if(ch == '_') { sb.append("__"); escaped = true; } else if(ch == ':' || (bFirst ? !isInitialNameChar(ch) : !isNameChar(ch))) { sb.append('_'); toHexChar(ch, sb); sb.append('_'); escaped = true; } else sb.append(ch); bFirst = false; } if(sb.length() == 0) sb.append('_'); else if(!escaped) return name; return sb.toString(); } public static String decodeFromQName(QName qname) { if(Util.isBlank(qname.getPrefix())) return decodeFromNCName(qname.getLocalPart()); else return qname.getPrefix() + "::" + decodeFromNCName(qname.getLocalPart()); } public static String decodeFromQNameSimple(QName qname) { if(Util.isBlank(qname.getPrefix())) return decodeFromNCNameSimple(qname.getLocalPart()); else return qname.getPrefix() + "::" + decodeFromNCNameSimple(qname.getLocalPart()); } public static String decodeFromNCNameSimple(String name) { StringBuffer sb = new StringBuffer(name.length() * 2); char[] chars = name.toCharArray(); for(char ch : chars) { if(ch == '_') ch = '-'; sb.append(ch); } return sb.toString(); } public static String encodeForNCNameSimple(String name) { StringBuffer sb = new StringBuffer(name.length() * 2); char[] chars = name.toCharArray(); boolean bFirst = true; for(char ch : chars) { if(bFirst ? isInitialNameChar(ch) : isNameChar(ch)) sb.append(ch); else sb.append('_'); bFirst = false; } return sb.toString(); } public static String decodeFromNCName(String name) { StringBuffer sb = new StringBuffer(name.length() * 2); boolean escaped = false; char chars[] = name.toCharArray(); for(int i = 0; i < chars.length; i++) { char ch = chars[i]; if(ch == '_') { escaped = true; char c = 0; i++; while(i < chars.length - 1) { if(chars[i] == '_') { break; } c <<= 8; int cn = fromHexChars(chars, i); if(cn >= 0) { c |= cn; i += 2; } else { mLogger.warn("Invalid encoded QName sequence {} {}", chars[i], chars[i + 1]); break; } } if(c == 0) sb.append('_'); else sb.append(c); } else sb.append(ch); } if(!escaped) return name; return sb.toString(); } public static boolean isNameChar(char ch) { if((ch == '-') | (ch == '.') | (ch == ':')) return true; if(ch < '0') return false; if(ch <= '9') return true; if(ch < 'A') return false; if(ch <= 'Z') return true; if(ch == '_') return true; if(ch < 'a') return false; if(ch <= 'z') return true; if(ch == 0xb7) return true; if(ch < 0xc0) return false; if(ch <= 0xd6) return true; if(ch < 0xd8) return false; if(ch <= 0xf6) return true; if(ch < 0xf8) return false; if(ch <= 0x37d) return true; if(ch < 0x37f) return false; if(ch <= 0x1fff) return true; if(ch < 0x200c) return false; if(ch <= 0x200d) return true; if(ch < 0x203f) return false; if(ch <= 0x2040) return true; if(ch < 0x2070) return false; if(ch <= 0x218f) return true; if(ch < 0x2c00) return false; if(ch <= 0x2fef) return true; if(ch < 0x3001) return false; if(ch <= 0xd7ff) return true; if(ch < 0xf900) return false; if(ch <= 0xfdcf) return true; if(ch < 0xfdf0) return false; if(ch <= 0xfffd) return true; return false; } public static boolean isInitialNameChar(char ch) { if(ch == ':') return true; if(ch < 'A') return false; if(ch <= 'Z') return true; if(ch == '_') return true; if(ch < 'a') return false; if(ch <= 'z') return true; if(ch < 0xc0) return false; if(ch <= 0xd6) return true; if(ch < 0xd8) return false; if(ch <= 0xf6) return true; if(ch < 0xf8) return false; if(ch <= 0x2ff) return true; if(ch < 0x370) return false; if(ch <= 0x37d) return true; if(ch < 0x37f) return false; if(ch <= 0x1fff) return true; if(ch < 0x200c) return false; if(ch <= 0x200d) return true; if(ch < 0x2070) return false; if(ch <= 0x218f) return true; if(ch < 0x2c00) return false; if(ch <= 0x2fef) return true; if(ch < 0x3001) return false; if(ch <= 0xd7ff) return true; if(ch < 0xf900) return false; if(ch <= 0xfdcf) return true; if(ch < 0xfdf0) return false; if(ch <= 0xfffd) return true; return false; } public static String readString(File file, SerializeOpts serializeOpts) throws IOException { InputStream is = new FileInputStream(file); String s = readString(is, serializeOpts.getInput_text_encoding()); is.close(); return s; } public static <T> String stringJoin(T[] list, String sep) { if(list == null) return "()"; StringBuilder sb = new StringBuilder(); for(T s : list) { if(sb.length() > 0) sb.append(sep); sb.append(s.toString()); } return sb.toString(); } public static String stringJoin(List<String> list, String sep) { if(list == null) return null; StringBuilder sb = new StringBuilder(); for(String s : list) { if(sb.length() > 0) sb.append(sep); sb.append(s); } return sb.toString(); } public static boolean isEmpty(Collection<?> list) { return list == null || list.isEmpty(); } public static String removeTrailingNewlines(String s, boolean ignorCR) { if(s == null) return null; int len = s.length(); int end = len - 1; while(end >= 0 && isNewline(s.charAt(end), ignorCR)) end--; if(end < len - 1) return s.substring(0, end + 1); else return s; } private static boolean isNewline(char c, boolean ignorCR) { return c == '\n' || (ignorCR && c == '\r'); } // Skip to c and return prefix, return null if c is never encountered before // EOF public static String skipToByte(InputStream is, int delim) throws IOException { if(is == null) return null; StringBuffer sb = new StringBuffer(); int c; while((c = is.read()) >= 0) { if(c == delim) return sb.toString(); sb.append((byte) c); } return null; } public static void safeRelease(IReleasable p) { if(p != null) { try { p.release(); } catch (Exception e) { mLogger.warn("Exception closing: " + p.getClass().getName(), e); } } } public static void safeClose(XMLEventReader reader) { if(reader != null) try { reader.close(); } catch (XMLStreamException e) { mLogger.warn("Exception closing reader", e); } } @SuppressWarnings("unchecked") public static <T extends Throwable> void wrapException(Throwable e, Class<T> cls) throws T { if(e.getClass().isInstance(cls)) throw (T) e; T ex = null; try { ex = JavaUtils.newObject(cls, e); } catch (Exception e1) { String msg = "Exception wrapping exception type: " + e.getClass().getName() + " to class: " + cls.getName(); mLogger.warn(msg, e1); throw new IllegalArgumentException(msg + e1.getMessage(), e); } throw ex; } public static <T extends Throwable> void wrapException(String message, Throwable e, Class<T> cls) throws T { // Need to add the message // if( e.getClass().isInstance(cls) ) // throw (T) e. ; T ex = null; try { ex = JavaUtils.newObject(cls, message, e); } catch (Exception e1) { String msg = "Exception wrapping exception type: " + e.getClass().getName() + " to class: " + cls.getName(); mLogger.warn(msg, e1); throw new IllegalArgumentException(message + ": " + msg + e1.getMessage(), e); } throw ex; } /* * Come up with a simple name from a complex one like a command */ public static String simpleName(String command, String def) { // Tokenize into words including path chars String words[] = command.split("[^\\w]+"); for(String w : words) { String name = FileUtils.basePathLikeName(w); // Convert paths java paths but dont use the path functions - might not // really be a path if(isBlank(name) || name.length() < 2) continue; // Trip off leading parts if looks like a path if(name.length() > 10) name = name.substring(0, 10) + "..."; return name; } return def; } /* * EnumSet helpers */ public static <T extends Enum<T>> boolean setContainsAll(EnumSet<T> set, T... items) { if(items.length == 0) return true; for(T item : items) // null item is ignored if(item != null && !set.contains(item)) return false; return true; } public static <T extends Enum<T>> boolean setContainsAny(Set<T> set, T... items) { if(set.size() == 0 || items.length == 0) return false; for(T e : items) if(e != null && set.contains(e)) return true; return false; } public static <T extends Enum<T>> boolean setContainsAny(Set<T> set, Set<T> items) { if(set.size() == 0) return false; if(items == null || items.size() == 0) return false; for(T e : items) if(e != null && set.contains(e)) return true; return false; } public static <T extends Enum<T>> EnumSet<T> enumSetOf(T e, T... enums) { EnumSet<T> set = EnumSet.of(e); if(enums.length > 0) for(T v : enums) { if(v != null) set.add(v); } return set; } public static <T extends Enum<T>> EnumSet<T> withEnumAdded(EnumSet<T> set, T on) { if(on == null || set.contains(on)) return set; set = set.clone(); set.add(on); return set; } public static <T extends Enum<T>> EnumSet<T> withEnumsAdded(EnumSet<T> set, EnumSet<T> on) { if(on.isEmpty()) return set; if(set.containsAll(on)) return set; set = set.clone(); set.addAll(on); return set; } public static <T extends Enum<T>> EnumSet<T> withEnumsAdded(EnumSet<T> set, T... on) { if(on.length == 0) return set; set = set.clone(); for(T v : on) if(v != null) set.add(v); return set; } public static <T extends Enum<T>> EnumSet<T> withEnumRemoved(EnumSet<T> set, T off) { if(off == null || set.isEmpty() || !set.contains(off)) return set; set = set.clone(); set.remove(off); return set; } public static <T extends Enum<T>> EnumSet<T> withEnumsRemoved(EnumSet<T> set, T... off) { if(set.isEmpty() || off.length == 0) return set; set = set.clone(); for(T e : off) if(e != null) set.remove(e); return set; } public static <T extends Enum<T>> EnumSet<T> withEnumsRemoved(EnumSet<T> set, EnumSet<T> off) { if(set.isEmpty() || off.isEmpty() || !setContainsAny(set, off)) return set; set = set.clone(); set.removeAll(off); return set; } // Return a new enum set with only those flags set in mask // same as interesection public static <T extends Enum<T>> EnumSet<T> withEnumsMasked(EnumSet<T> set, EnumSet<T> mask) { if(set.isEmpty()) return set; set = set.clone(); set.retainAll(mask); return set; } public static <T extends Enum<T>> EnumSet<T> withEnumsMasked(EnumSet<T> set, T first, T... mask) { return withEnumsMasked(set, EnumSet.of(first, mask)); } public static void wrapIOException(Throwable e) throws IOException { if(e instanceof IOException) throw (IOException) e; throw new IOException(e); } public static void wrapIOException(String message, Throwable e) throws IOException { throw new IOException(message, e); } public static void wrapCoreException(String message, Throwable e) throws CoreException { throw mLogger.throwing(new CoreException(message, e)); } public static void safeClose(XMLEventWriter closable) { try { if(closable != null) closable.close(); } catch (Exception e) { mLogger.info("Exception closing: " + closable.getClass().getName(), e); } } // Calculate a new wait time given a current wait time // if waitTime = 0 then means forever (return 0) // if waitTime < 0 means no wait public static long nextWait(long end, long waitTime) { if(waitTime <= 0) return waitTime; long now = System.currentTimeMillis(); if(now >= end) return -1; return end - now; } public static String joinValues(List<XValue> args, String sep) { return join(args, sep); } // Iterator from start to ... end (exclusive) public static Iterator<String> rangeIterator(final int start, final int end) { return new Iterator<String>() { int pos = start; @Override public boolean hasNext() { return start < end; } @Override public String next() { return String.valueOf(pos++); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } public static Iterator<String> stringIterator( final Iterator<Integer> iterator) { return new Iterator<String>() { Iterator<Integer> iter = iterator; @Override public boolean hasNext() { return iter.hasNext(); } @Override public String next() { return iter.next().toString(); } @Override public void remove() { iter.remove(); } }; } public static String join(Collection<?> args) { return join(args, ","); } public static String join(Collection<?> args, String sep) { return join(new StringBuilder(), args, sep).toString(); } public static StringBuilder join(StringBuilder sb, Collection<?> args) { return join(sb, args, ","); } public static StringBuilder join(StringBuilder sb, Collection<?> args, String sep) { for(Object arg : args) { if(sb.length() > 0) sb.append(sep); sb.append(arg.toString()); } return sb; } public static <T> List<T> toList(Iterator<T> iter) { List<T> list = new ArrayList<T>(); while(iter.hasNext()) list.add(iter.next()); return list; } public static <E> Iterator<E> singletonIterator(final E e) { return new Iterator<E>() { private boolean hasNext = true; @Override public boolean hasNext() { return hasNext; } @Override public E next() { if(hasNext) { hasNext = false; return e; } throw new NoSuchElementException(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } public static <E> Iterable<E> toIterable(final Iterator<E> iter) { return new Iterable<E>() { @Override public Iterator<E> iterator() { return iter; } }; } public static <S, D> Iterator<D> toConvertingIterater( Iterator<S> iter, ITypeConverter<S, D> converter) { return new TypeConvertingIterator<S, D>(iter, converter); } // An iterator that converts from <S>ource type to <D> type public static <S, D> Iterable<D> toConvertingIterable( final Iterator<S> iterator, final ITypeConverter<S, D> converter) { return new Iterable<D>() { @Override public Iterator<D> iterator() { return toConvertingIterater(iterator, converter); } }; } public static boolean isOneOf(String s, String... strings) { for(String of : strings) if(Util.isEqual(s, of)) return true; return false; } public static Reader toReader(String string) { return new StringReader(string); } public static Reader toReader(URL scriptURL, String inputTextEncoding) throws IOException { return new InputStreamReader(scriptURL.openStream(), inputTextEncoding); } public static <T> T[] toArray(T... v) { return v; } public static <T> boolean contains(T[] array, T v) { for(T a : array) if(a.equals(v)) return true; return false; } public static <T> ILogValue traceArray(final T[] array) { return new ILogValue() { public String toString() { return "[" + Util.stringJoin(array, ",") + "]"; } }; } public static String stringConcat(String... values) { StringBuilder sb = new StringBuilder(); for(String s : values) sb.append(s); return sb.toString(); } // Safe conversion in known encoding public static byte[] stringToAsciiBytes(String name) { return name.getBytes(java.nio.charset.StandardCharsets.US_ASCII); } public static byte[] stringToUTF8Bytes(String name) { return name.getBytes(java.nio.charset.StandardCharsets.UTF_8); } public static <E extends Enum<E>> StringBuilder logString(StringBuilder sb, EnumSet<E> set) { return join(sb, set, ","); } public static <T> List<T> appendList(List<T> list1, List<T> list2) { if(list1 == null || list1.isEmpty()) return list2; if(list2 == null || list2.isEmpty()) return list1; int n = list1.size() + list2.size(); List<T> list = new ArrayList<T>(n); list.addAll(list1); list.addAll(list2); return list; } public static String stringJoin(String string, String sep, int length) { StringBuilder sb = new StringBuilder(string); while(--length > 0) sb.append(sep).append(string); return sb.toString(); } public static void require(boolean test, String message) throws UnexpectedException { if(!test) throw new UnexpectedException(message); } public static INamingStrategy getNamingStrategy(String strategy) { switch(strategy){ case "json": case "full": return INamingStrategy.DefaultNamingStrategy; case "simple": case "default": return INamingStrategy.SimpleNamingStrategy; case "none": case "local": case "null": default: return INamingStrategy.LocalNamingStrategy; } } public static String escape(String string) { return escape(string,kESCAPE, CharAttrs.NONE); } public static String escape(String string, char escape,CharAttr attr) { return escape( string,escape,CharAttrs.constInstance(attr)); } public static String escape(String string, CharAttr attr) { return escape( string,kESCAPE,CharAttrs.constInstance(attr)); } public static String escape(String string, char escape,CharAttrs attrs) { if(string.indexOf(escape) < 0) return string; return CharAttributeBuffer.encodeString(string,escape, attrs).decodeString(escape); } } // // // Copyright (C) 2008-2014 David A. Lee. // // The contents of this file are subject to the "Simplified BSD License" (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.opensource.org/licenses/bsd-license.php // // Software distributed under the License is distributed on an "AS IS" basis, // WITHOUT WARRANTY OF ANY KIND, either express or implied. // See the License for the specific language governing rights and limitations // under the License. // // The Original Code is: all this file. // // The Initial Developer of the Original Code is David A. Lee // // Portions created by (your name) are Copyright (C) (your legal entity). All // Rights Reserved. // // Contributor(s): none. //