diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /component/src/main/java/com/yahoo/component/ComponentId.java |
Publish
Diffstat (limited to 'component/src/main/java/com/yahoo/component/ComponentId.java')
-rw-r--r-- | component/src/main/java/com/yahoo/component/ComponentId.java | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/component/src/main/java/com/yahoo/component/ComponentId.java b/component/src/main/java/com/yahoo/component/ComponentId.java new file mode 100644 index 00000000000..50417c15b5e --- /dev/null +++ b/component/src/main/java/com/yahoo/component/ComponentId.java @@ -0,0 +1,230 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.component; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * The id of a component. + * Consists of a name, optionally a version, and optionally a namespace. + * This is an immutable value object. + * + * @author bratseth + * @author tonytv + */ +public final class ComponentId implements Comparable<ComponentId> { + + private final class VersionHandler implements Spec.VersionHandler<Version> { + + @Override + public Version emptyVersion() { + return Version.emptyVersion; + } + + @Override + public int compare(Version v1, Version v2) { + return v1.compareTo(v2); + } + } + + private final Spec<Version> spec; + private final boolean anonymous; + private final static class Counter { + private int count = 0; + public int getAndIncrement() { return count++; } + } + private static ThreadLocal<Counter> gid = new ThreadLocal<Counter>() { + @Override protected Counter initialValue() { + return new Counter(); + } + }; + private static AtomicInteger uniqueTid = new AtomicInteger(0); + private static ThreadLocal<String> tid = new ThreadLocal<String>() { + @Override protected String initialValue() { + return new String("_"+uniqueTid.getAndIncrement()+"_"); + } + }; + + /** Precomputed string value */ + private final String stringValue; + + private ComponentId(String name, Version version, ComponentId namespace, boolean anonymous) { + if (anonymous) { + name = createAnonymousName(name); + } + spec = new Spec<>(new VersionHandler(), name, version, namespace); + this.anonymous = anonymous; + + stringValue = spec.createStringValue(); + } + + private String createAnonymousName(String name) { + return new StringBuilder(name).append(tid.get()).append(gid.get().getAndIncrement()).toString(); + } + + public ComponentId(String name, Version version, ComponentId namespace) { + this(name, version, namespace, false); + } + + /** Creates a component id from a name and version. The version may be null */ + public ComponentId(String name, Version version) { + this(name, version, null); + } + + /** + * Creates a component id from the id string form: name(:version)?(@namespace)?, + * where version has the form 1(.2(.3(.identifier)?)?)? + * and namespace is a component id + */ + public ComponentId(String id) { + this(new SpecSplitter(id)); + } + + private ComponentId(SpecSplitter splitter) { + this(splitter.name, Version.fromString(splitter.version), splitter.namespace); + } + + public ComponentId nestInNamespace(ComponentId namespace) { + if (namespace == null) { + return this; + } else { + ComponentId newNamespace = getNamespace() == null ? + namespace : + getNamespace().nestInNamespace(namespace); + return new ComponentId(getName(), getVersion(), newNamespace); + } + } + + /** Returns the name of this. This is never null */ + public String getName() { return spec.name; } + + /** Returns the version of this id, or emptyVersion if no version is specified */ + public Version getVersion() { return spec.version; } + + /** The namespace is null if this is a top level component id **/ + public ComponentId getNamespace() { return spec.namespace; } + + /** + * Returns the string value of this id. + * If no version is given, this is simply the name. + * If a version is given, it is name:version. + * Trailing ".0"'s are stripped from the version part. + */ + public String stringValue() { return stringValue; } + + public @Override String toString() { + return spec.toString(); + } + + public boolean equals(Object o) { + if (o==this) return true; + if ( ! (o instanceof ComponentId)) return false; + + ComponentId c = (ComponentId) o; + if (isAnonymous() || c.isAnonymous()) + return false; + + return c.stringValue().equals(stringValue); + } + + public @Override int hashCode() { + return stringValue.hashCode(); + } + + public ComponentSpecification toSpecification() { + if (isAnonymous()) + throw new RuntimeException("Can't generate a specification for an anonymous component id."); + return new ComponentSpecification(getName(), + getVersion().toSpecification(), getNamespace()); + } + + public int compareTo(ComponentId other) { + //anonymous must never be equal to non-anonymous + if (isAnonymous() ^ other.isAnonymous()) { + return isAnonymous() ? -1 : 1; + } + + return spec.compareTo(other.spec); + } + + /** + * Creates a componentId that is unique for this run-time instance + */ + // TODO: Check if we really need this. -JB + public static ComponentId createAnonymousComponentId(String baseName) { + return new ComponentId(baseName, null, null, true); + } + + public boolean isAnonymous() { + return anonymous; + } + + /** Returns a copy of this id with namespace set to null **/ + public ComponentId withoutNamespace() { + return new ComponentId(getName(), getVersion(), null); + } + + /** + * Creates a component id from the id string form: name(:version)?(@namespace)?, + * where version has the form 1(.2(.3(.identifier)?)?)? + * and namespace is a component id. + * + * @return new ComponentId(componentId), or null if the input string is null + */ + public static ComponentId fromString(String componentId) { + try { + return (componentId != null) ? new ComponentId(componentId) : null; + } catch(Exception e) { + throw new IllegalArgumentException("Illegal component id: '" + componentId + "'", e); + } + } + + /** + * Returns this id's stringValue (i.e the id without trailing ".0"'s) translated to a file name using the + * <i>standard translation:</i> + * <pre><code> + * : → - + * / → _ + * </code></pre> + */ + public String toFileName() { + return stringValue.replace(":","-").replace("/","."); + } + + /** + * Creates an id from a file <b>first</b> name string encoded in the standard translation (see {@link #toFileName}). + * <b>Note</b> that any file last name, like e.g ".xml" must be stripped off before handoff to this method. + */ + public static ComponentId fromFileName(final String fileName) { + // Initial assumptions + String id=fileName; + Version version =null; + ComponentId namespace=null; + + // Split out namespace, if any + int at=id.indexOf("@"); + if (at>0) { + String newId=id.substring(0,at); + namespace=ComponentId.fromString(id.substring(at+1)); + id=newId; + } + + // Split out version, if any + int dash=id.lastIndexOf("-"); + if (dash>0) { + String newId=id.substring(0,dash); + try { + version=new Version(id.substring(dash+1)); + id=newId; + } + catch (IllegalArgumentException e) { + // don't interpret the text following the dash as a version + } + } + + // Convert dots in id portion back - this is the part which prevents us from + id=id.replace(".","/"); + + return new ComponentId(id,version,namespace); + } + +} |