Java tutorial
/* * This work is Open Source and licensed by the European Commission under the * conditions of the European Public License v1.1 * * (http://www.osor.eu/eupl/european-union-public-licence-eupl-v.1.1); * * any use of this file implies acceptance of the conditions of this license. * 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 eu.eidas.auth.commons.attribute; import java.util.Map; import java.util.Properties; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.xml.namespace.QName; import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Ordering; import org.apache.commons.lang.StringUtils; import eu.eidas.auth.commons.collections.PrintSortedProperties; import eu.eidas.auth.commons.io.PropertiesConverter; import eu.eidas.auth.commons.lang.Canonicalizers; import eu.eidas.auth.commons.lang.EnumMapper; import eu.eidas.auth.commons.lang.KeyAccessor; import eu.eidas.util.Preconditions; /** * Converts a Properties into a Set of AttributeDefinition. * <p> * The Properties format is defined as in the following XML Properties example: * <p> * <pre> * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> * <properties> * <comment>eIDAS attributes</comment> * <entry key="1.Name">"http://eidas.europa.eu/attributes/naturalperson/PersonIdentifier</entry> * <entry key="1.FriendlyName">PersonIdentifier</entry> * <entry key="1.PersonType">NaturalPerson</entry> * <entry key="1.Required">true</entry> * <entry key="1.UniqueIdentifier">true</entry> * <entry key="1.XmlType.NamespaceUri">http://eidas.europa.eu/attributes/naturalperson</entry> * <entry key="1.XmlType.LocalPart">PersonIdentifierType</entry> * <entry key="1.XmlType.NamespacePrefix">eidas-natural</entry> * <entry key="1.AttributeValueMarshaller">eu.eidas.auth.commons.attribute.impl.UnrestrictedStringAttributeValue</entry> * * <entry key="2.Name">http://eidas.europa.eu/attributes/naturalperson/CurrentFamilyName</entry> * <entry key="2.FriendlyName">FamilyName</entry> * <entry key="2.PersonType">NaturalPerson</entry> * <entry key="2.Required">true</entry> * <entry key="2.TransliterationMandatory">true</entry> * <entry key="2.XmlType.NamespaceUri">http://eidas.europa.eu/attributes/naturalperson</entry> * <entry key="2.XmlType.LocalPart">CurrentFamilyNameType</entry> * <entry key="2.XmlType.NamespacePrefix">eidas-natural</entry> * <entry key="2.AttributeValueMarshaller">eu.eidas.auth.commons.attribute.impl.StringAttributeValueMarshaller</entry> * * <entry key="3.Name">http://eidas.europa.eu/attributes/naturalperson/CurrentGivenName</entry> * <entry key="3.FriendlyName">FirstName</entry> * <entry key="3.PersonType">NaturalPerson</entry> * <entry key="3.Required">true</entry> * <entry key="3.TransliterationMandatory">true</entry> * <entry key="3.XmlType.NamespaceUri">http://eidas.europa.eu/attributes/naturalperson</entry> * <entry key="3.XmlType.LocalPart">CurrentGivenNameType</entry> * <entry key="3.XmlType.NamespacePrefix">eidas-natural</entry> * <entry key="3.AttributeValueMarshaller">eu.eidas.auth.commons.attribute.impl.StringAttributeValueMarshaller</entry> * </properties> * </pre> * * @since 1.1 */ public enum AttributeSetPropertiesConverter implements PropertiesConverter<ImmutableSortedSet<AttributeDefinition<?>>> { INSTANCE; private enum Suffix { NAME_URI(".NameUri"), FRIENDLY_NAME(".FriendlyName"), PERSON_TYPE(".PersonType"), REQUIRED(".Required"), TRANSLITERATION_MANDATORY(".TransliterationMandatory"), UNIQUE_IDENTIFIER(".UniqueIdentifier"), XML_TYPE_NAMESPACE_URI(".XmlType.NamespaceUri"), XML_TYPE_LOCAL_PART(".XmlType.LocalPart"), XML_TYPE_NAMESPACE_PREFIX(".XmlType.NamespacePrefix"), ATTRIBUTE_VALUE_MARSHALLER(".AttributeValueMarshaller"); private static final EnumMapper<String, Suffix> MAPPER = new EnumMapper<String, Suffix>( new KeyAccessor<String, Suffix>() { @Nonnull @Override public String getKey(@Nonnull Suffix suffix) { return suffix.getValue(); } }, Canonicalizers.trimLowerCase(), values()); @Nullable public static Suffix fromString(@Nonnull String val) { return MAPPER.fromKey(val); } public static EnumMapper<String, Suffix> mapper() { return MAPPER; } @Nonnull private final transient String value; private final transient int length; Suffix(@Nonnull String value) { this.value = value; this.length = value.length(); } @Nonnull public String computeEntry(@Nonnull String key) { Preconditions.checkNotBlank(key, "key"); return key + value; } @Nonnull public String extractKeyPrefix(@Nonnull String entry) { if (!matches(entry)) { throw new IllegalArgumentException( "entry \"" + entry + "\" does not match suffix \"" + value + "\""); } return entry.substring(0, entry.length() - length); } public int getLength() { return length; } @Nonnull public String getValue() { return value; } public boolean matches(@Nullable String entry) { return null != entry && entry.endsWith(value); } @Override public String toString() { return getValue(); } } @Nonnull private static String getMandatoryString(@Nonnull Map<?, ?> properties, @Nonnull Suffix suffix, @Nonnull String id) { String key = suffix.computeEntry(id); String value = (String) properties.get(key); if (StringUtils.isBlank(value)) { throw new IllegalArgumentException("Entry \"" + key + "\" has an empty value"); } return value; } private static boolean getOptionalFlag(@Nonnull Map<?, ?> properties, @Nonnull Suffix suffix, @Nonnull String id) { String key = suffix.computeEntry(id); String value = (String) properties.get(key); if (StringUtils.isNotBlank(value)) { boolean flag = "true".equalsIgnoreCase(value); if (!flag && !"false".equalsIgnoreCase(value)) { throw new IllegalArgumentException("Entry \"" + key + "\" has an illegal value \"" + value + "\""); } return flag; } return false; } /** * Converts the given properties into an immutable sorted set of <code>AttributeDefinition</code>s. * * @param properties the properties * @return an immutable sorted set of <code>AttributeDefinition</code>s. */ @SuppressWarnings("CollectionDeclaredAsConcreteClass") @Nonnull public static ImmutableSortedSet<AttributeDefinition<?>> toAttributeSet(@Nonnull Properties properties) { return toAttributeSetFromProperties(properties); } /** * Converts the given Map into an immutable sorted set of <code>AttributeDefinition</code>s. * * @param properties the Map * @return an immutable sorted set of <code>AttributeDefinition</code>s. */ @Nonnull public static ImmutableSortedSet<AttributeDefinition<?>> toAttributeSet( @Nonnull Map<String, String> properties) { return toAttributeSetFromProperties(properties); } @Nonnull private static ImmutableSortedSet<AttributeDefinition<?>> toAttributeSetFromProperties( @Nonnull Map<?, ?> properties) { ImmutableSortedSet.Builder<AttributeDefinition<?>> setBuilder = new ImmutableSortedSet.Builder<AttributeDefinition<?>>( Ordering.natural()); for (Map.Entry<?, ?> entry : properties.entrySet()) { String key = (String) entry.getKey(); if (Suffix.NAME_URI.matches(key)) { String id = Suffix.NAME_URI.extractKeyPrefix(key); String fullName = getMandatoryString(properties, Suffix.NAME_URI, id); String friendlyName = getMandatoryString(properties, Suffix.FRIENDLY_NAME, id); boolean required = getOptionalFlag(properties, Suffix.REQUIRED, id); boolean transliterationMandatory = getOptionalFlag(properties, Suffix.TRANSLITERATION_MANDATORY, id); boolean uniqueIdentifier = getOptionalFlag(properties, Suffix.UNIQUE_IDENTIFIER, id); String typeStr = getMandatoryString(properties, Suffix.PERSON_TYPE, id); PersonType personType = PersonType.fromString(typeStr); if (null == personType) { throw new IllegalArgumentException("Entry \"" + Suffix.PERSON_TYPE.computeEntry(id) + "\" has an illegal value \"" + typeStr + "\""); } String xmlNamespaceUri = getMandatoryString(properties, Suffix.XML_TYPE_NAMESPACE_URI, id); String localPart = getMandatoryString(properties, Suffix.XML_TYPE_LOCAL_PART, id); String xmlNamespacePrefix = getMandatoryString(properties, Suffix.XML_TYPE_NAMESPACE_PREFIX, id); String attributeValueMarshaller = getMandatoryString(properties, Suffix.ATTRIBUTE_VALUE_MARSHALLER, id); AttributeDefinition.Builder<?> builder = AttributeDefinition.builder(); builder.nameUri(fullName); builder.friendlyName(friendlyName); builder.personType(personType); builder.required(required); builder.transliterationMandatory(transliterationMandatory); builder.uniqueIdentifier(uniqueIdentifier); builder.xmlType(xmlNamespaceUri, localPart, xmlNamespacePrefix); builder.attributeValueMarshaller(attributeValueMarshaller); setBuilder.add(builder.build()); } } return setBuilder.build(); } /** * Converts the given Iterable of <code>AttributeDefinition</code>s to an immutable sorted Map of Strings. * * @param properties the Iterable of <code>AttributeDefinition</code>s * @return an immutable sorted Map of Strings. */ @Nonnull public static ImmutableSortedMap<String, String> toMap(@Nonnull Iterable<AttributeDefinition<?>> attributes) { ImmutableSortedMap.Builder<String, String> builder = new ImmutableSortedMap.Builder<String, String>( Ordering.natural()); int i = 0; for (AttributeDefinition<?> attributeDefinition : attributes) { String id = String.valueOf(++i); builder.put(Suffix.NAME_URI.computeEntry(id), attributeDefinition.getNameUri().toASCIIString()); builder.put(Suffix.FRIENDLY_NAME.computeEntry(id), attributeDefinition.getFriendlyName()); builder.put(Suffix.PERSON_TYPE.computeEntry(id), attributeDefinition.getPersonType().getValue()); builder.put(Suffix.REQUIRED.computeEntry(id), String.valueOf(attributeDefinition.isRequired())); if (attributeDefinition.isTransliterationMandatory()) { builder.put(Suffix.TRANSLITERATION_MANDATORY.computeEntry(id), Boolean.TRUE.toString()); } if (attributeDefinition.isUniqueIdentifier()) { builder.put(Suffix.UNIQUE_IDENTIFIER.computeEntry(id), Boolean.TRUE.toString()); } QName xmlType = attributeDefinition.getXmlType(); builder.put(Suffix.XML_TYPE_NAMESPACE_URI.computeEntry(id), String.valueOf(xmlType.getNamespaceURI())); builder.put(Suffix.XML_TYPE_LOCAL_PART.computeEntry(id), String.valueOf(xmlType.getLocalPart())); builder.put(Suffix.XML_TYPE_NAMESPACE_PREFIX.computeEntry(id), String.valueOf(xmlType.getPrefix())); builder.put(Suffix.ATTRIBUTE_VALUE_MARSHALLER.computeEntry(id), attributeDefinition.getAttributeValueMarshaller().getClass().getName()); } return builder.build(); } /** * Converts the given Iterable of <code>AttributeDefinition</code>s to a Properties. * * @param properties the Iterable of <code>AttributeDefinition</code>s * @return a Properties. */ @Nonnull public static Properties toProperties(@Nonnull Iterable<AttributeDefinition<?>> attributes) { ImmutableSortedMap<String, String> sortedMap = toMap(attributes); Properties properties = new PrintSortedProperties(); //noinspection UseOfPropertiesAsHashtable properties.putAll(sortedMap); return properties; } @Nonnull @Override public Properties marshal(@Nonnull ImmutableSortedSet<AttributeDefinition<?>> value) { return toProperties(value); } @Nonnull @Override public ImmutableSortedSet<AttributeDefinition<?>> unmarshal(@Nonnull Properties properties) { return toAttributeSet(properties); } }