/* * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.model.common.expression.functions; import java.io.File; import java.io.IOException; import java.text.Normalizer; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.naming.InvalidNameException; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import javax.xml.datatype.XMLGregorianCalendar; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.DateUtils; import org.w3c.dom.Element; import org.w3c.dom.Node; import com.evolveum.midpoint.model.common.expression.script.ScriptExpression; import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluationContext; import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.schema.constants.MidPointConstants; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.processor.ResourceAttribute; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.schema.util.ShadowUtil; import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.DebugDumpable; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; /** * Library of standard midPoint functions. These functions are made available to all * midPoint expressions. * * The functions should be written to support scripting-like comfort. It means that they all needs * to be null-safe, automatically convert data types as necessary and so on. * * @author Radovan Semancik * */ public class BasicExpressionFunctions { public static final String NAME_SEPARATOR = " "; public static final Trace LOGGER = TraceManager.getTrace(BasicExpressionFunctions.class); private static String STRING_PATTERN_WHITESPACE = "\\s+"; private static String STRING_PATTERN_HONORIFIC_PREFIX_ENDS_WITH_DOT = "^(\\S+\\.)$"; private static Pattern PATTERN_NICK_NAME = Pattern.compile("^([^\"]*)\"([^\"]+)\"([^\"]*)$"); private PrismContext prismContext; private Protector protector; public BasicExpressionFunctions(PrismContext prismContext, Protector protector) { super(); this.prismContext = prismContext; this.protector = protector; } /** * Convert string to lower case. */ public static String lc(String orig) { return StringUtils.lowerCase(orig); } /** * Convert string to upper case. */ public static String uc(String orig) { return StringUtils.upperCase(orig); } public boolean contains(Object object, Object search) { String objectStr = stringify(object); if (StringUtils.isEmpty(objectStr)) { return false; } String searchStr = stringify(search); if (StringUtils.isEmpty(searchStr)) { return false; } return objectStr.contains(searchStr); } public boolean containsIgnoreCase(Object object, Object search) { String objectStr = stringify(object); if (StringUtils.isEmpty(objectStr)) { return false; } String searchStr = stringify(search); if (StringUtils.isEmpty(searchStr)) { return false; } return StringUtils.containsIgnoreCase(objectStr, searchStr); } /** * Remove whitespaces at the beginning and at the end of the string. */ public static String trim(String orig) { return StringUtils.trim(orig); } /** * Concatenates the arguments to create a name. * Each argument is stringified, trimmed and the result is concatenated by spaces. */ public String concatName(Object... components) { if (components == null || components.length == 0) { return ""; } boolean endsWithSeparator = false; StringBuilder sb = new StringBuilder(); for (int i = 0; i < components.length; i++) { Object component = components[i]; if (component == null) { continue; } String stringComponent = stringify(component); if (stringComponent == null) { continue; } String trimmedStringComponent = trim(stringComponent); if (trimmedStringComponent.isEmpty()) { continue; } sb.append(trimmedStringComponent); if (i < (components.length - 1)) { sb.append(NAME_SEPARATOR); endsWithSeparator = true; } else { endsWithSeparator = false; } } if (endsWithSeparator) { sb.delete(sb.length() - NAME_SEPARATOR.length(), sb.length()); } return sb.toString(); } /** * Normalize a string value. It follows the default normalization algorithm * used for PolyString values. * * @param orig original value to normalize * @return normalized value */ public String norm(String orig) { if (orig == null){ return null; } PolyString polyString = new PolyString(orig); polyString.recompute(prismContext.getDefaultPolyStringNormalizer()); return polyString.getNorm(); } /** * Normalize a PolyString value. * * @param orig original value to normalize * @return normalized value */ public String norm(PolyString orig) { if (orig == null){ return null; } if (orig.getNorm() != null) { return orig.getNorm(); } orig.recompute(prismContext.getDefaultPolyStringNormalizer()); return orig.getNorm(); } /** * Normalize a PolyStringType value. * * @param orig original value to normalize * @return normalized value */ public String norm(PolyStringType orig) { if (orig == null){ return null; } PolyString polyString = orig.toPolyString(); return norm(polyString); } public String toAscii(Object input) { if (input == null) { return null; } String inputString = stringify(input); String decomposed = Normalizer.normalize(inputString, Normalizer.Form.NFKD); return decomposed.replaceAll("\\p{M}", ""); } /** * Converts whatever it gets to a string. But it does it in a sensitive way. * E.g. it tries to detect collections and returns the first element (if there is only one). * Never returns null. Returns empty string instead. */ public String stringify(Object whatever) { if (whatever == null) { return ""; } if (whatever instanceof String) { return (String)whatever; } if (whatever instanceof PolyString) { return ((PolyString)whatever).getOrig(); } if (whatever instanceof PolyStringType) { return ((PolyStringType)whatever).getOrig(); } if (whatever instanceof Collection) { Collection collection = (Collection)whatever; if (collection.isEmpty()) { return ""; } if (collection.size() > 1) { throw new IllegalArgumentException("Cannot stringify collection because it has "+collection.size()+" values"); } whatever = collection.iterator().next(); } Class<? extends Object> whateverClass = whatever.getClass(); if (whateverClass.isArray()) { Object[] array = (Object[])whatever; if (array.length == 0) { return ""; } if (array.length > 1) { throw new IllegalArgumentException("Cannot stringify array because it has "+array.length+" values"); } whatever = array[0]; } if (whatever == null) { return ""; } if (whatever instanceof String) { return (String)whatever; } if (whatever instanceof PolyString) { return ((PolyString)whatever).getOrig(); } if (whatever instanceof PolyStringType) { return ((PolyStringType)whatever).getOrig(); } if (whatever instanceof Element) { Element element = (Element)whatever; Element origElement = DOMUtil.getChildElement(element, PolyString.F_ORIG); if (origElement != null) { // This is most likely a PolyStringType return origElement.getTextContent(); } else { return element.getTextContent(); } } if (whatever instanceof Node) { return ((Node)whatever).getTextContent(); } return whatever.toString(); } public Collection<String> getOids(Collection<ObjectReferenceType> refs){ if (refs == null){ return null; } Collection<String> oids = new ArrayList<String>(); for (ObjectReferenceType ort : refs){ if (StringUtils.isNotBlank(ort.getOid())){ oids.add(ort.getOid()); } else if (ort.asReferenceValue().getObject() != null && StringUtils.isNotBlank(ort.asReferenceValue().getObject().getOid())){ oids.add(ort.asReferenceValue().getObject().getOid()); } } return oids; } public Collection<String> getOids(ObjectReferenceType refs){ List<ObjectReferenceType> refList = new ArrayList<>(); refList.add(refs); return getOids(refList); } public Collection<String> getOids(ObjectType refs){ List<String> oid = new ArrayList<>(); oid.add(refs.getOid()); return oid; } public boolean isEmpty(Object whatever) { if (whatever == null) { return true; } if (whatever instanceof String) { return ((String)whatever).isEmpty(); } if (whatever instanceof Collection) { return ((Collection)whatever).isEmpty(); } String whateverString = stringify(whatever); if (whateverString == null) { return true; } return whateverString.isEmpty(); } public <T> Collection<T> getExtensionPropertyValues(ObjectType object, String namespace, String localPart) { return getExtensionPropertyValues(object, new javax.xml.namespace.QName(namespace, localPart)); } public <T> Collection<T> getExtensionPropertyValues(ObjectType object, groovy.xml.QName propertyQname) { return getExtensionPropertyValues(object, propertyQname.getNamespaceURI(), propertyQname.getLocalPart()); } public <T> Collection<T> getExtensionPropertyValues(ObjectType object, javax.xml.namespace.QName propertyQname) { return ObjectTypeUtil.getExtensionPropertyValuesNotNull(object, propertyQname); } public <T> T getExtensionPropertyValue(ObjectType object, String namespace, String localPart) throws SchemaException { return getExtensionPropertyValue(object, new javax.xml.namespace.QName(namespace, localPart)); } public Referencable getExtensionReferenceValue(ObjectType object, String namespace, String localPart) throws SchemaException { return getExtensionReferenceValue(object, new javax.xml.namespace.QName(namespace, localPart)); } public <T> T getExtensionPropertyValue(ObjectType object, groovy.xml.QName propertyQname) throws SchemaException { return getExtensionPropertyValue(object, propertyQname.getNamespaceURI(), propertyQname.getLocalPart()); } public <T> T getExtensionPropertyValue(ObjectType object, javax.xml.namespace.QName propertyQname) throws SchemaException { if (object == null) { return null; } Collection<T> values = ObjectTypeUtil.getExtensionPropertyValues(object, propertyQname); return toSingle(values, "a multi-valued extension property "+propertyQname); } public Referencable getExtensionReferenceValue(ObjectType object, javax.xml.namespace.QName propertyQname) throws SchemaException { if (object == null) { return null; } Collection<Referencable> values = ObjectTypeUtil.getExtensionReferenceValues(object, propertyQname); return toSingle(values, "a multi-valued extension property "+propertyQname); } public <T> T getPropertyValue(ObjectType object, String path) throws SchemaException { Collection<T> values = getPropertyValues(object, path); return toSingle(values, "a multi-valued property "+path); } public <T> Collection<T> getPropertyValues(ObjectType object, String path) { if (object == null) { return null; } ScriptExpressionEvaluationContext scriptContext = ScriptExpressionEvaluationContext.getThreadLocal(); ScriptExpression scriptExpression = scriptContext.getScriptExpression(); ItemPath itemPath = scriptExpression.parsePath(path); PrismProperty property = object.asPrismObject().findProperty(itemPath); if (property == null) { return new ArrayList<T>(0); } return property.getRealValues(); } public <T> Collection<T> getAttributeValues(ShadowType shadow, String attributeNamespace, String attributeLocalPart) { return getAttributeValues(shadow, new javax.xml.namespace.QName(attributeNamespace, attributeLocalPart)); } public <T> Collection<T> getAttributeValues(ShadowType shadow, String attributeLocalPart) { return getAttributeValues(shadow, new javax.xml.namespace.QName(MidPointConstants.NS_RI, attributeLocalPart)); } public <T> Collection<T> getAttributeValues(ShadowType shadow, groovy.xml.QName attributeQname) { return getAttributeValues(shadow, attributeQname.getNamespaceURI(), attributeQname.getLocalPart()); } public <T> Collection<T> getAttributeValues(ShadowType shadow, javax.xml.namespace.QName attributeQname) { return ShadowUtil.getAttributeValues(shadow, attributeQname); } public <T> T getAttributeValue(ShadowType shadow, String attributeNamespace, String attributeLocalPart) throws SchemaException { return getAttributeValue(shadow, new javax.xml.namespace.QName(attributeNamespace, attributeLocalPart)); } public <T> T getAttributeValue(ShadowType shadow, String attributeLocalPart) throws SchemaException { return getAttributeValue(shadow, new javax.xml.namespace.QName(MidPointConstants.NS_RI, attributeLocalPart)); } public <T> T getAttributeValue(ShadowType shadow, groovy.xml.QName attributeQname) throws SchemaException { return getAttributeValue(shadow, attributeQname.getNamespaceURI(), attributeQname.getLocalPart()); } public <T> T getAttributeValue(ShadowType shadow, javax.xml.namespace.QName attributeQname) throws SchemaException { return ShadowUtil.getAttributeValue(shadow, attributeQname); } public Collection<String> getAttributeStringValues(ShadowType shadow, String attributeNamespace, String attributeLocalPart) { return getAttributeStringValues(shadow, new javax.xml.namespace.QName(attributeNamespace, attributeLocalPart)); } public Collection<String> getAttributeStringValues(ShadowType shadow, groovy.xml.QName attributeQname) { return getAttributeStringValues(shadow, attributeQname.getNamespaceURI(), attributeQname.getLocalPart()); } public Collection<String> getAttributeStringValues(ShadowType shadow, javax.xml.namespace.QName attributeQname) { return ShadowUtil.getAttributeValues(shadow, attributeQname, String.class); } public <T> T getIdentifierValue(ShadowType shadow) throws SchemaException { if (shadow == null) { return null; } Collection<ResourceAttribute<?>> identifiers = ShadowUtil.getPrimaryIdentifiers(shadow); if (identifiers.size() == 0) { return null; } if (identifiers.size() > 1) { throw new SchemaException("More than one idenfier in "+shadow); } Collection<T> realValues = (Collection<T>) identifiers.iterator().next().getRealValues(); if (realValues.size() == 0) { return null; } if (realValues.size() > 1) { throw new SchemaException("More than one idenfier value in "+shadow); } return realValues.iterator().next(); } public <T> T getSecondaryIdentifierValue(ShadowType shadow) throws SchemaException { if (shadow == null) { return null; } Collection<ResourceAttribute<?>> identifiers = ShadowUtil.getSecondaryIdentifiers(shadow); if (identifiers.size() == 0) { return null; } if (identifiers.size() > 1) { throw new SchemaException("More than one secondary idenfier in "+shadow); } Collection<T> realValues = (Collection<T>) identifiers.iterator().next().getRealValues(); if (realValues.size() == 0) { return null; } if (realValues.size() > 1) { throw new SchemaException("More than one secondary idenfier value in "+shadow); } return realValues.iterator().next(); } public String determineLdapSingleAttributeValue(Collection<String> dns, String attributeName, PrismProperty attribute) throws NamingException { return determineLdapSingleAttributeValue(dns, attributeName, attribute.getRealValues()); } public <T> T getResourceIcfConfigurationPropertyValue(ResourceType resource, javax.xml.namespace.QName propertyQname) throws SchemaException { if (propertyQname == null) { return null; } PrismContainer<?> configurationProperties = getIcfConfigurationProperties(resource); if (configurationProperties == null) { return null; } PrismProperty<T> property = configurationProperties.findProperty(propertyQname); if (property == null) { return null; } return property.getRealValue(); } public <T> T getResourceIcfConfigurationPropertyValue(ResourceType resource, String propertyLocalPart) throws SchemaException { if (propertyLocalPart == null) { return null; } PrismContainer<?> configurationProperties = getIcfConfigurationProperties(resource); if (configurationProperties == null) { return null; } for (PrismProperty<?> property: configurationProperties.getValue().getProperties()) { if (propertyLocalPart.equals(property.getElementName().getLocalPart())) { return (T) property.getAnyRealValue(); } } return null; } private PrismContainer<?> getIcfConfigurationProperties(ResourceType resource) { if (resource == null) { return null; } PrismContainer<?> connectorConfiguration = resource.asPrismObject().findContainer(ResourceType.F_CONNECTOR_CONFIGURATION); if (connectorConfiguration == null) { return null; } return connectorConfiguration.findContainer(SchemaConstants.ICF_CONFIGURATION_PROPERTIES); } public String determineLdapSingleAttributeValue(Collection<String> dns, String attributeName, Collection<String> values) throws NamingException { if (values == null || values.isEmpty()) { // Shortcut. This is maybe the most common case. We want to return quickly and we also need to avoid more checks later. return null; } if (dns == null || dns.isEmpty()) { return determineLdapSingleAttributeValue((String)null, attributeName, values); } if (dns.size() > 1) { throw new IllegalArgumentException("Nore than one value ("+dns.size()+" for dn argument specified"); } return determineLdapSingleAttributeValue(dns.iterator().next(), attributeName, values); } // We cannot have Collection<String> here. The generic type information will disappear at runtime and the scripts can pass // anything that they find suitable. E.g. XPath is passing elements public String determineLdapSingleAttributeValue(String dn, String attributeName, Collection<?> values) throws NamingException { if (values == null || values.isEmpty()) { return null; } Collection<String> stringValues = null; // Determine item type, try to convert to strings Object firstElement = values.iterator().next(); if (firstElement instanceof String) { stringValues = (Collection)values; } else if (firstElement instanceof Element) { stringValues = new ArrayList<String>(values.size()); for (Object value: values) { Element element = (Element)value; stringValues.add(element.getTextContent()); } } else { throw new IllegalArgumentException("Unexpected value type "+firstElement.getClass()); } if (stringValues.size() == 1) { return stringValues.iterator().next(); } if (StringUtils.isBlank(dn)) { throw new IllegalArgumentException("No dn argument specified, cannot determine which of "+values.size()+" values to use"); } LdapName parsedDn = new LdapName(dn); for (int i=0; i < parsedDn.size(); i++) { Rdn rdn = parsedDn.getRdn(i); Attributes rdnAttributes = rdn.toAttributes(); NamingEnumeration<String> rdnIDs = rdnAttributes.getIDs(); while (rdnIDs.hasMore()) { String rdnID = rdnIDs.next(); Attribute attribute = rdnAttributes.get(rdnID); if (attributeName.equals(attribute.getID())) { for (int j=0; j < attribute.size(); j++) { Object value = attribute.get(j); if (stringValues.contains(value)) { return (String) value; } } } } } // Fallback. No values in DN. Just return the first alphabetically-wise value. return Collections.min(stringValues); } public <T> T toSingle(Collection<T> values) throws SchemaException { if (values == null || values.isEmpty()) { return null; } else if (values.size() > 1) { throw new SchemaException("Attempt to get single value from a multi-valued property"); } else { return values.iterator().next(); } } private <T> T toSingle(Collection<T> values, String contextDesc) throws SchemaException { if (values == null || values.isEmpty()) { return null; } else if (values.size() > 1) { throw new SchemaException("Attempt to get single value from " + contextDesc); } else { return values.iterator().next(); } } public static String readFile(String filename) throws IOException { return FileUtils.readFileToString(new File(filename)); } public String formatDateTime(String format, XMLGregorianCalendar xmlCal) { if (xmlCal == null || format == null) { return null; } SimpleDateFormat sdf = new SimpleDateFormat(format); Date date = XmlTypeConverter.toDate(xmlCal); return sdf.format(date); } public String formatDateTime(String format, Long millis) { if (millis == null || format == null) { return null; } SimpleDateFormat sdf = new SimpleDateFormat(format); return sdf.format(millis); } public XMLGregorianCalendar parseDateTime(String format, String stringDate) throws ParseException { if (format == null || stringDate == null) { return null; } String[] formats = new String[]{format}; Date date = DateUtils.parseDate(stringDate, formats); if (date == null) { return null; } return XmlTypeConverter.createXMLGregorianCalendar(date); } public XMLGregorianCalendar currentDateTime() { return XmlTypeConverter.createXMLGregorianCalendar(System.currentTimeMillis()); } private ParsedFullName parseFullName(String fullName) { if (StringUtils.isBlank(fullName)) { return null; } String root = fullName.trim(); ParsedFullName p = new ParsedFullName(); // LOGGER.trace("(1) root=", root); Matcher m = PATTERN_NICK_NAME.matcher(root); if (m.matches()) { String nickName = m.group(2).trim(); p.setNickName(nickName); root = m.group(1) + " " + m.group(3); // LOGGER.trace("nick={}, root={}", nickName, root); } String[] words = root.split(STRING_PATTERN_WHITESPACE); int i = 0; // LOGGER.trace("(2) i={}, words={}", i, Arrays.toString(words)); StringBuilder honorificPrefixBuilder = new StringBuilder(); while (i < words.length && words[i].matches(STRING_PATTERN_HONORIFIC_PREFIX_ENDS_WITH_DOT)) { honorificPrefixBuilder.append(words[i]); honorificPrefixBuilder.append(" "); i++; } if (honorificPrefixBuilder.length() > 0) { honorificPrefixBuilder.setLength(honorificPrefixBuilder.length() - 1); p.setHonorificPrefix(honorificPrefixBuilder.toString()); } // LOGGER.trace("(3) i={}, words={}", i, Arrays.toString(words)); List<String> rootNameWords = new ArrayList<>(); while (i < words.length && !words[i].endsWith(",")) { rootNameWords.add(words[i]); i++; } if (i < words.length && words[i].endsWith(",")) { String word = words[i]; i++; if (!word.equals(",")) { word = word.substring(0, word.length() - 1); rootNameWords.add(word); } } // LOGGER.trace("(4) i={}, words={}", i, Arrays.toString(words)); // LOGGER.trace("(4) rootNameWords={}", rootNameWords); if (rootNameWords.size() > 1) { p.setFamilyName(rootNameWords.get(rootNameWords.size() - 1)); rootNameWords.remove(rootNameWords.size() - 1); p.setGivenName(rootNameWords.get(0)); rootNameWords.remove(0); p.setAdditionalName(StringUtils.join(rootNameWords, " ")); } else if (rootNameWords.size() == 1) { p.setFamilyName(rootNameWords.get(0)); } StringBuilder honorificSuffixBuilder = new StringBuilder(); while (i < words.length) { honorificSuffixBuilder.append(words[i]); honorificSuffixBuilder.append(" "); i++; } if (honorificSuffixBuilder.length() > 0) { honorificSuffixBuilder.setLength(honorificSuffixBuilder.length() - 1); p.setHonorificSuffix(honorificSuffixBuilder.toString()); } LOGGER.trace("Parsed full name '{}' as {}", fullName, p); return p; } public String parseGivenName(Object fullName) { ParsedFullName p = parseFullName(stringify(fullName)); if (p == null) { return null; } else { return p.getGivenName(); } } public String parseFamilyName(Object fullName) { ParsedFullName p = parseFullName(stringify(fullName)); if (p == null) { return null; } else { return p.getFamilyName(); } } public String parseAdditionalName(Object fullName) { ParsedFullName p = parseFullName(stringify(fullName)); if (p == null) { return null; } else { return p.getAdditionalName(); } } public String parseNickName(Object fullName) { ParsedFullName p = parseFullName(stringify(fullName)); if (p == null) { return null; } else { return p.getNickName(); } } public String parseHonorificPrefix(Object fullName) { ParsedFullName p = parseFullName(stringify(fullName)); if (p == null) { return null; } else { return p.getHonorificPrefix(); } } public String parseHonorificSuffix(Object fullName) { ParsedFullName p = parseFullName(stringify(fullName)); if (p == null) { return null; } else { return p.getHonorificSuffix(); } } public String decrypt(ProtectedStringType protectedString) { try { return protector.decryptString(protectedString); } catch (EncryptionException e) { throw new SystemException(e.getMessage(), e); } } public ProtectedStringType encrypt(String string) { try { return protector.encryptString(string); } catch (EncryptionException e) { throw new SystemException(e.getMessage(), e); } } /** * Creates a valid LDAP distinguished name from the wide range of components. The method * can be invoked in many ways, e.g.: * * composeDn("cn","foo","o","bar") * composeDn("cn","foo",new Rdn("o","bar")) * composeDn(new Rdn("cn","foo"),"ou","baz",new Rdn("o","bar")) * composeDn(new Rdn("cn","foo"),"ou","baz","o","bar") * composeDn(new Rdn("cn","foo"),new LdapName("ou=baz,o=bar")) * composeDn("cn","foo",new LdapName("ou=baz,o=bar")) * * Note: the DN is not normalized. The case of the attribute names and white spaces are * preserved. */ public static String composeDn(Object... components) throws InvalidNameException { if (components == null) { return null; } if (components.length == 0) { return null; } if (components.length == 1 && components[0] == null) { return null; } if (components.length == 1 && (components[0] instanceof String) && StringUtils.isBlank((String)(components[0]))) { return null; } LinkedList<Rdn> rdns = new LinkedList<>(); String attrName = null; for (Object component: components) { if (attrName != null && !(component instanceof String || component instanceof PolyString || component instanceof PolyStringType)) { throw new InvalidNameException("Invalid input to composeDn() function: expected string after '"+attrName+"' argument, but got "+component.getClass()); } if (component instanceof Rdn) { rdns.addFirst((Rdn)component); } else if (component instanceof PolyString) { component = ((PolyString)component).toString(); } else if (component instanceof PolyStringType) { component = ((PolyStringType)component).toString(); } if (component instanceof String) { if (attrName == null) { attrName = (String)component; } else { rdns.addFirst(new Rdn(attrName, (String)component)); attrName = null; } } if (component instanceof LdapName) { rdns.addAll(0,((LdapName)component).getRdns()); } } LdapName dn = new LdapName(rdns); return dn.toString(); } /** * Creates a valid LDAP distinguished name from the wide range of components assuming that * the last component is a suffix. The method can be invoked in many ways, e.g.: * * composeDn("cn","foo","o=bar") * composeDn(new Rdn("cn","foo"),"ou=baz,o=bar") * composeDn(new Rdn("cn","foo"),new LdapName("ou=baz,o=bar")) * composeDn("cn","foo",new LdapName("ou=baz,o=bar")) * * The last element is a complete suffix represented either as String or LdapName. * * Note: the DN is not normalized. The case of the attribute names and white spaces are * preserved. */ public static String composeDnWithSuffix(Object... components) throws InvalidNameException { if (components == null) { return null; } if (components.length == 0) { return null; } if (components.length == 1) { if (components[0] == null) { return null; } if ((components[0] instanceof String)) { if (StringUtils.isBlank((String)(components[0]))) { return null; } else { return (new LdapName((String)(components[0]))).toString(); } } else if ((components[0] instanceof LdapName)) { return ((LdapName)(components[0])).toString(); } else { throw new InvalidNameException("Invalid input to composeDn() function: expected suffix (last element) to be String or LdapName, but it was "+components[0].getClass()); } } Object suffix = components[components.length - 1]; if (suffix instanceof String) { suffix = new LdapName((String)suffix); } if (!(suffix instanceof LdapName)) { throw new InvalidNameException("Invalid input to composeDn() function: expected suffix (last element) to be String or LdapName, but it was "+suffix.getClass()); } components[components.length - 1] = suffix; return composeDn(components); } public static String debugDump(Object o) { if (o == null) { return "null"; } if (o instanceof ObjectType) { return DebugUtil.debugDump(((ObjectType)o).asPrismObject(), 0); } return DebugUtil.debugDump(o, 0); } public static String debugDump(Object o, int indent) { if (o == null) { return "null"; } if (o instanceof ObjectType) { return DebugUtil.debugDump(((ObjectType)o).asPrismObject(), indent); } return DebugUtil.debugDump(o, indent); } }