global.namespace.truelicense.api.x500.GenX500PrincipalBuilder Maven / Gradle / Ivy
/*
* Copyright (C) 2005 - 2019 Schlichtherle IT Services.
* All rights reserved. Use is subject to license terms.
*/
package global.namespace.truelicense.api.x500;
import global.namespace.truelicense.api.builder.GenBuilder;
import javax.security.auth.x500.X500Principal;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Pattern;
import static java.util.Objects.requireNonNull;
/**
* A generic builder for an {@link X500Principal}.
*
* This abstract class is provided so that sub-classes can easily add methods
* which return {@code this} without the need to cast its type by clients.
* Example:
*
* {@code
* class MyBuilder extends GenX500PrincipalBuilder {
* MyBuilder addFOO(String value) {
* addKeyword("FOO", "1.2.3.4.5.6.7.8.9");
* return addAttribute("FOO", value);
* }
* }
*
* class MyClient {
* X500Principal create() {
* return new MyBuilder()
* .addAttribute("CN", "Duke")
* .addFOO("a")
* .build();
* }
* }
* }
*
* Notice that the call to {@code addFOO} is compilable because the previous
* call to {@code addAttribute} is defined to return the generic type parameter
* {@code This}.
*
* @param the type of this generic X.500 principal builder.
* @since TrueLicense 2.3
*/
public abstract class GenX500PrincipalBuilder>
implements GenBuilder {
private final Map
attributes = new LinkedHashMap<>(),
keywords = new HashMap<>();
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#C C}, value)
.
*/
public This addC(String value) { return addAttribute(X500AttributeTypeKeyword.C, value); }
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#CN CN}, value)
.
*/
public This addCN(String value) { return addAttribute(X500AttributeTypeKeyword.CN, value); }
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#DC DC}, value)
.
*/
public This addDC(String value) { return addAttribute(X500AttributeTypeKeyword.DC, value); }
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#DNQ DNQ}, value)
.
*/
public This addDNQ(String value) { return addAttribute(X500AttributeTypeKeyword.DNQ, value); }
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#DNQUALIFIER DNQUALIFIER}, value)
.
*/
public This addDNQUALIFIER(String value) {
return addAttribute(X500AttributeTypeKeyword.DNQUALIFIER, value);
}
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#EMAILADDRESS EMAILADDRESS}, value)
.
*/
public This addEMAILADDRESS(String value) {
return addAttribute(X500AttributeTypeKeyword.EMAILADDRESS, value);
}
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#GENERATION GENERATION}, value)
.
*/
public This addGENERATION(String value) {
return addAttribute(X500AttributeTypeKeyword.GENERATION, value);
}
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#GIVENNAME GIVENNAME}, value)
.
*/
public This addGIVENNAME(String value) {
return addAttribute(X500AttributeTypeKeyword.GIVENNAME, value);
}
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#INITIALS INITIALS}, value)
.
*/
public This addINITIALS(String value) {
return addAttribute(X500AttributeTypeKeyword.INITIALS, value);
}
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#L L}, value)
.
*/
public This addL(String value) { return addAttribute(X500AttributeTypeKeyword.L, value); }
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#O O}, value)
.
*/
public This addO(String value) { return addAttribute(X500AttributeTypeKeyword.O, value); }
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#OU OU}, value)
.
*/
public This addOU(String value) { return addAttribute(X500AttributeTypeKeyword.OU, value); }
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#SERIALNUMBER SERIALNUMBER}, value)
.
*/
public This addSERIALNUMBER(String value) {
return addAttribute(X500AttributeTypeKeyword.SERIALNUMBER, value);
}
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#ST ST}, value)
.
*/
public This addST(String value) { return addAttribute(X500AttributeTypeKeyword.ST, value); }
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#STREET STREET}, value)
.
*/
public This addSTREET(String value) { return addAttribute(X500AttributeTypeKeyword.STREET, value); }
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#SURNAME SURNAME}, value)
.
*/
public This addSURNAME(String value) {
return addAttribute(X500AttributeTypeKeyword.SURNAME, value);
}
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#T T}, value)
.
*/
public This addT(String value) { return addAttribute(X500AttributeTypeKeyword.T, value); }
/**
* Equivalent to
* addAttribute({@link X500AttributeTypeKeyword#UID UID}, value)
.
*/
public This addUID(String value) { return addAttribute(X500AttributeTypeKeyword.UID, value); }
/**
* Adds an X.500 attribute to the distinguished name.
*
* @param keyword the attribute type keyword.
* @param value the nullable, unquoted and unescaped attribute value.
* @return {@code this}
*/
public This addAttribute(X500AttributeTypeKeyword keyword, String value) {
return addAttribute(keyword.name(), value);
}
/**
* Adds an X.500 attribute to the distinguished name.
*
* @param type the attribute type,
* which can be referenced by a keyword or its OID.
* @param value the nullable, unquoted and unescaped attribute value.
* @return {@code this}
* @see #addKeyword
*/
@SuppressWarnings("unchecked")
public This addAttribute(final String type, final String value) {
requireNonNull(type);
if (null != value)
attributes.put(type, value);
else
attributes.remove(type);
return (This) this;
}
/**
* Adds a mapping from a keyword to an Object Identifier (OID).
*
* @param keyword the attribute type keyword.
* @param oid the nullable OID.
* @return {@code this}
*/
@SuppressWarnings("unchecked")
public This addKeyword(final String keyword, final String oid) {
requireNonNull(keyword);
if (null != oid)
keywords.put(keyword, oid);
else
keywords.remove(keyword);
return (This) this;
}
/**
* Returns the X.500 principal with the distinguished name as built from
* the sequence of attribute type and value pairs.
*
* @throws IllegalArgumentException if an attribute type is unknown or its
* keyword mapping is invalid.
*/
public X500Principal build() {
final DistinguishedNameAssembler dna = new DistinguishedNameAssembler();
for (Map.Entry e : attributes.entrySet())
dna.appendAttribute(e.getKey(), e.getValue());
return new X500Principal(dna.toString(), keywords);
}
private static final class DistinguishedNameAssembler {
private static final String QUOTED_CHARACTERS_SEQUENCE = ",+<>;";
private static final Pattern QUOTED_CHARACTERS_PATTERN =
Pattern.compile(".*[" + QUOTED_CHARACTERS_SEQUENCE + "].*");
private final StringBuilder dname = new StringBuilder();
void appendAttribute(final String type, final String value) {
// See http://www.ietf.org/rfc/rfc2253.txt
delimitAttribute();
dname.append(type).append('=');
final boolean nq = needsQuoting(value);
if (nq) dname.append('"');
dname.append(escapeBackslashAndQuote(value));
if (nq) dname.append('"');
}
private void delimitAttribute() {
if (0 < dname.length()) dname.append(',');
}
private static boolean needsQuoting(String value) {
return !value.equals(value.trim()) ||
value.startsWith("#") ||
QUOTED_CHARACTERS_PATTERN.matcher(value).matches();
}
private static String escapeBackslashAndQuote(String value) {
return value.replace("\\", "\\\\").replace("\"", "\\\"");
}
@Override public String toString() { return dname.toString(); }
}
}