// ============================================================================ // // Copyright (C) 2006-2016 Talend Inc. - www.talend.com // // This source code is available under agreement available at // %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt // // You should have received a copy of the agreement // along with this program; if not, write to Talend SA // 9 rue Pages 92150 Suresnes, France // // ============================================================================ package routines; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; /* * user specification: the function's comment should contain keys as follows: 1. write about the function's comment.but * it must be before the "{talendTypes}" key. * * 2. {talendTypes} 's value must be talend Type, it is required . its value should be one of: String, char | Character, * long | Long, int | Integer, boolean | Boolean, byte | Byte, Date, double | Double, float | Float, Object, short | * Short * * 3. {Category} define a category for the Function. it is required. its value is user-defined . * * 4. {param} 's format is: {param} <type>[(<default value or closed list values>)] <name>[ : <comment>] * * <type> 's value should be one of: string, int, list, double, object, boolean, long, char, date. <name>'s value is the * Function's parameter name. the {param} is optional. so if you the Function without the parameters. the {param} don't * added. you can have many parameters for the Function. * * 5. {example} gives a example for the Function. it is optional. */ public class MDM { /** * getFK: Return one of the FK component by position in a mangled FK (FKs are mangled in MDM to accommodate for * compound keys) * * * {talendTypes} String * * {Category} MDM * * {param} string(FKs) mangledFK: original mangled FK. * * {param} int(0) pos: key position (starts at 0) * * {example} getFK(FKs,0) # 12345 */ public static String getFK(String mangledFK, int pos) { if (mangledFK == null) { return null; } Pattern p = Pattern.compile("(\\[[^\\[\\]]*\\])"); //$NON-NLS-1$ Matcher m = p.matcher(mangledFK.trim()); int i = 0; while (m.find()) { if (i == pos) { String targetValue = m.group(0); return targetValue.substring(1, targetValue.length() - 1); } i++; } return null; } /** * createFK: Returns the mangled FK string of a given key (FKs are mangled in MDM to accommodate for compound keys). * Returns null if key is null. * * * {talendTypes} String * * {Category} MDM * * {param} string singleKey: original key. * * * {example} createFK("0") # return "[0]" */ public static String createFK(String singleKey) { if (singleKey != null) { return "[" + singleKey + "]"; //$NON-NLS-1$ //$NON-NLS-2$ } else { return null; } } /** * createFK: Returns the mangled FK string of a given array of keys (FKs are mangled in MDM to accommodate for * compound keys). Returns null if one of the keys is null. * * * {talendTypes} String * * {Category} MDM * * {param} string singleKey: original key array. * * * {example} createFK({"0","1"}) # return "[0][1]" */ public static String createFK(String[] keys) { StringBuffer sb = new StringBuffer(); for (String key : keys) { if (key == null) { return null; } sb.append("[").append(key).append("]"); //$NON-NLS-1$ //$NON-NLS-2$ } return sb.toString(); } /** * get repeating element in xmlString according to the xpath & position * * {talendTypes} String * * {Category} MDM * * {param} string(xml) xml: xml * * {param} string(xpath) xpath: xpath. * * {param} int(0) position: position. */ public static String getRepeatingElement(String xml, String xpath, int position) throws Exception { Node node = parse(xml); NodeList list = getNodeList(node, xpath, false); for (int i = 0; i < list.getLength(); i++) { if (i == position) { Node n = list.item(i); return n.getNodeValue(); } } return null; } /** * check repeating elements in xmlString according to xpath & text * * {talendTypes} Boolean * * {Category} MDM * * {param} string(xml) xml: xml. * * {param} string(xpath) xpath: xpath. * * {param} String(text) text: text. */ public static boolean hasRepeatingElement(String xml, String xpath, String text) throws Exception { Node node = parse(xml); NodeList list = getNodeList(node, xpath, false); for (int i = 0; i < list.getLength(); i++) { Node n = list.item(i); if (n.getNodeValue().equals(text)) { return true; } } return false; } /** * list repeating elements in xmlString according to xpath & delimiter * * {talendTypes} String * * {Category} MDM * * {param} string(xml) xml: xml. * * {param} string(xpath) xpath: xpath. * * {param} char(delimiter) delimiter: delimiter. */ public static String listRepeatingElement(String xml, String xpath, char delimiter) throws Exception { Node node = parse(xml); NodeList list = getNodeList(node, xpath, false); StringBuffer sb = new StringBuffer(); for (int i = 0; i < list.getLength(); i++) { Node n = list.item(i); sb.append(n.getNodeValue()); if (i >= 0 && i < list.getLength() - 1) { sb.append(delimiter); } } return sb.toString(); } /** * add repeating elements in xmlString according to xpath & text * * {talendTypes} String * * {Category} MDM * * {param} string(xml) xml: xml * * {param} string(xpath) xpath: xpath * * {param} String(text) text: text */ public static String addRepeatingElement(String xml, String xpath, String text) throws Exception { Node node = parse(xml); int pos = xpath.lastIndexOf('/'); String name = xpath.substring(pos + 1); String parentPath = xpath.substring(0, pos); NodeList plist = getNodeList(node, parentPath, true); if (plist.getLength() > 0) { Element el = node.getOwnerDocument().createElement(name); el.setTextContent(text); plist.item(0).appendChild(el); } return nodeToString(node, true); } /** * @deprecated Generate an <error code="X">msg</error> fragment * * * {talendTypes} String * * {Category} MDM * * {param} string msg: error message. * * {param} int(0) code: error code, (1:ERROR, 0:NORMAL) * * {example} genErrMsg("test message",0) # return <error code="0">test message</error> */ @Deprecated public static String createReturnMessage(String msg, int code) { return "<error code=\"" + code + "\">" + msg + "</error>"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ } /** * Generate an <report><message type="X">msg</message><report> fragment * * * {talendTypes} String * * {Category} MDM * * {param} string msg: error message. * * {param} String(0) type: error code, (info|error) * * {example} genErrMsg("test message",0) # return <error code="0">test message</error> */ public static String createReturnMessage(String msg, String type) { return "<report><message type=\"" + type + "\">" + msg + "</message></report>"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ } /** * Add or update an ISO variant to the multi-lingual text value * * * {talendTypes} String * * {Category} MDM * * {param} string(iso) iso: iso * * {param} string(value) value: value * * {param} string(rawValue) rawValue: rawValue * * {example} setLanguageVariant("EN","abc","[EN:ab][FR:ab_fr]") # return [EN:abc][FR:ab_fr] */ public static String setLanguageVariant(String iso, String value, String rawValue) { return setLanguageVariant(iso, value, rawValue, "EN", true); //$NON-NLS-1$ } /** * Add or update an ISO variant to the multi-lingual text value with defaultIso and sort option (For the legacy * value which do not follow the multi-lingual field syntax, it will be adapted to defaultIso) * * {talendTypes} String * * {Category} MDM * * {param} string(iso) iso: iso * * {param} string(value) value: value * * {param} string(rawValue) rawValue: rawValue * * {param} string(defaultIso) defaultIso: defaultIso * * {param} string(sort) sort: sort * * {example} setLanguageVariant("FR","ab_fr","ab","EN", true) # return [EN:ab][FR:ab_fr] */ public static String setLanguageVariant(String iso, String value, String rawValue, String defaultIso, boolean sort) { if (iso == null || value == null) { throw new IllegalArgumentException(); } iso = iso.toUpperCase(); Map<String, String> isoValues = new LinkedHashMap<String, String>(); if (rawValue == null || rawValue.trim().length() == 0) { isoValues.put(iso, value); } else { Pattern p = Pattern.compile("\\[(\\w+)\\:([^\\[\\]]*?)\\]{1,}"); //$NON-NLS-1$ Matcher m = p.matcher(rawValue); while (m.find()) { isoValues.put(m.group(1).toUpperCase(), m.group(2)); } // illegal/legacy raw value if (isoValues.size() == 0) { // throw new IllegalArgumentException(); if (defaultIso != null && defaultIso.trim().length() > 0) { isoValues.put(defaultIso.toUpperCase(), rawValue); } else { isoValues.put("EN", rawValue); //$NON-NLS-1$ } } isoValues.put(iso, value); } StringBuilder result = new StringBuilder(); if (isoValues.size() > 0) { List<String> isoList = new ArrayList<String>(isoValues.keySet()); // sort if (sort) { Collections.sort(isoList); } for (String string : isoList) { String isoKey = string; String isoValue = isoValues.get(isoKey); result.append("[").append(isoKey).append(":").append(isoValue).append("]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } return result.toString(); } /** * Give an ISO value from a multi-lingual text value, with default iso fallback * * {talendTypes} String * * {Category} MDM * * {param} string(iso) iso: iso * * {param} string(defaultIso) defaultIso: Default iso used in case of fallback * * {param} string(rawValue) rawValue: rawValue * * {example} getLanguageVariant("DE","EN","[EN:ab][FR:ab_fr]") # return [EN:ab] */ public static String getLanguageVariant(String iso, String defaultIso, String rawValue) { String requestedLanguageVariant = getLanguageVariant(iso, rawValue); if (requestedLanguageVariant == null || "".equals(requestedLanguageVariant)) { //$NON-NLS-1$ // fallback to the default variant return getLanguageVariant(defaultIso, rawValue); } else { return requestedLanguageVariant; } } /** * Give an ISO value from a multi-lingual text value * * * {talendTypes} String * * {Category} MDM * * {param} string(iso) iso: iso * * {param} string(rawValue) rawValue: rawValue * * {example} getLanguageVariant("FR","[EN:ab][FR:ab_fr]") # return ab_fr */ public static String getLanguageVariant(String iso, String rawValue) { if (iso == null || rawValue == null) { throw new IllegalArgumentException(); } iso = iso.toUpperCase(); Map<String, String> isoValues = new HashMap<String, String>(); Pattern p = Pattern.compile("\\[(\\w+)\\:([^\\[\\]]*?)\\]{1,}"); //$NON-NLS-1$ Matcher m = p.matcher(rawValue); while (m.find()) { isoValues.put(m.group(1).toUpperCase(), m.group(2)); } return isoValues.get(iso); } // Utility methods /** * Get a nodelist from an xPath * * @throws Exception */ private static NodeList getNodeList(Node contextNode, String xPath, boolean isParent) throws Exception { if (!xPath.matches(".*@[^/\\]]+")) { //$NON-NLS-1$ if (!xPath.endsWith(")") && !isParent) { //$NON-NLS-1$ xPath += "/text()"; //$NON-NLS-1$ } } XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); XPathExpression expr = xpath.compile(xPath); Object result = expr.evaluate(contextNode, XPathConstants.NODESET); NodeList nodes = (NodeList) result; return nodes; } /** * parse the xml * * @param xml * @return * @throws Exception */ private static Node parse(String xml) throws Exception { DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); domFactory.setNamespaceAware(true); // never forget this! DocumentBuilder builder = domFactory.newDocumentBuilder(); Document doc = builder.parse(new InputSource(new StringReader(xml))); return doc.getDocumentElement(); } /** * Generates an xml string from a node with or without the xml declaration (not pretty formatted) * * @param n the node * @return the xml string * @throws TransformerException */ private static String nodeToString(Node n, boolean omitXMLDeclaration) throws TransformerException { StringWriter sw = new StringWriter(); Transformer transformer = TransformerFactory.newInstance().newTransformer(); if (omitXMLDeclaration) { transformer.setOutputProperty("omit-xml-declaration", "yes"); //$NON-NLS-1$ //$NON-NLS-2$ } else { transformer.setOutputProperty("omit-xml-declaration", "no"); //$NON-NLS-1$ //$NON-NLS-2$ } transformer.setOutputProperty("indent", "yes"); //$NON-NLS-1$ //$NON-NLS-2$ transformer.transform(new DOMSource(n), new StreamResult(sw)); return sw.toString().replaceAll("\r\n", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ } }