blob: 3c8bda55f823d717858b978ba75fe343550c60f7 [file] [log] [blame]
/*
* Copyright 2015 The Apache Software Foundation.
*
* 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 org.apache.brooklyn.util.osgi;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Comparator;
import javax.annotation.Nullable;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.text.BrooklynVersionSyntax;
import org.apache.brooklyn.util.text.NaturalOrderComparator;
import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.text.VersionComparator;
import org.osgi.framework.Bundle;
import org.osgi.framework.Version;
import com.google.common.base.Objects;
import com.google.common.collect.ComparisonChain;
/** Records a name (string) and version (string),
* with conveniences for pretty-printing and converting to OSGi format. */
@JsonInclude(Include.NON_EMPTY)
public class VersionedName implements Comparable<VersionedName> {
private final String name;
private final String v;
@Deprecated // since 0.12.0 - but null was not omitted in jackson serialization, so remove these fields after 1.2
private final String symbolicName = null;
@Deprecated // since 0.12.0 - but null was not omitted in jackson serialization, so remove these fields after 1.2
private final Version version = null;
public VersionedName(Bundle b) {
this(b.getSymbolicName(), b.getVersion().toString());
}
public VersionedName(String name, String v) {
this.name = checkNotNull(name, "name");
this.v = v;
}
public VersionedName(String name, @Nullable Version v) {
this.name = checkNotNull(name, "name").toString();
this.v = v==null ? null : v.toString();
}
@Override
public String toString() {
return name + ":" + v;
}
public String toOsgiString() {
return name + ":" + getOsgiVersion();
}
public boolean equals(String sn, String v) {
return name.equals(sn) && Objects.equal(this.v, v);
// could also consider equal if the osgi mapping is equal (but for now we don't; if caller wants that, they should create Version)
// || (v!=null && Version.parseVersion(BrooklynVersionSyntax.toValidOsgiVersion(v)).equals(getOsgiVersion())));
}
public boolean equals(String sn, Version v) {
return name.equals(sn) && Objects.equal(getOsgiVersion(), v);
}
public String getSymbolicName() {
return name;
}
private transient Version cachedOsgiVersion;
@Nullable
public Version getOsgiVersion() {
if (cachedOsgiVersion==null && v!=null) {
cachedOsgiVersion = v==null ? null : Version.parseVersion(BrooklynVersionSyntax.toValidOsgiVersion(v));
}
return cachedOsgiVersion;
}
@Nullable
public String getOsgiVersionString() {
Version ov = getOsgiVersion();
if (ov==null) return null;
return ov.toString();
}
@Nullable
public String getVersionString() {
return v;
}
@Deprecated /** @deprecated since 0.12.0 use {@link #getVersionString()} or {@link #getOsgiVersion()} */
public Version getVersion() {
return getOsgiVersion();
}
@Override
public int hashCode() {
return Objects.hashCode(name, v);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof VersionedName)) {
return false;
}
VersionedName o = (VersionedName) other;
return Objects.equal(name, o.name) && Objects.equal(v, o.v);
}
/** As {@link #equals(Object)} but accepting the argument as equal
* if versions are identical under the {@link #getOsgiVersion()} conversion;
* also accepts strings as the other, converting as per {@link #fromString(String)} */
public boolean equalsOsgi(Object other) {
if (other instanceof String) {
other = VersionedName.fromString((String)other);
}
if (!(other instanceof VersionedName)) {
return false;
}
VersionedName o = (VersionedName) other;
return Objects.equal(name, o.name) && Objects.equal(getOsgiVersion(), o.getOsgiVersion());
}
/** Returns a {@link VersionedName} instance where {@link #getVersionString()} complies with OSGi syntax (or is null),
* even if the input does not.
* Typically used to convert from Brooklyn-recommended <code>1.0-SNAPSHOT</code> syntax to
* OSGi-mandated <code>1.0.0.SNAPSHOT</code> syntax. */
public static VersionedName toOsgiVersionedName(VersionedName vn) {
if (vn==null) return null;
return new VersionedName(vn.getSymbolicName(), vn.getOsgiVersion());
}
/** As {@link #parseMaybe(String, boolean)} but throwing if invalid; allows null version */
public static VersionedName fromString(String vn) {
return parseMaybe(vn, false).get();
}
/** Takes a string which might be of the form "symbolic-name" or "symbolic-name:version" (or something else entirely)
* and returns a VersionedName. The versionedName.getVersion() will be null if if there was no version in the input
* and the second argument is false, versions not required.
* Returns a {@link Maybe#absent()} with a suitable message if not valid. */
public static Maybe<VersionedName> parseMaybe(String symbolicNameWithVersion, boolean versionRequired) {
if (Strings.isBlank(symbolicNameWithVersion)) {
return Maybe.absent("Identifier is blank");
}
String[] parts = symbolicNameWithVersion.split(":");
if (versionRequired && parts.length!=2) {
return Maybe.absent("Identifier '"+symbolicNameWithVersion+"' must be of 'name:version' syntax");
}
if (parts.length > 2) {
return Maybe.absent("Identifier '"+symbolicNameWithVersion+"' has too many parts; max one ':' symbol");
}
return Maybe.of(new VersionedName(parts[0], parts.length == 2 ? parts[1] : null));
}
@Override
public int compareTo(VersionedName other) {
return VersionedNameComparator.INSTANCE.compare(this, other);
}
/** Comparator which puts a:3 < a:1 < b:2 < null */
public static class VersionedNameComparator implements Comparator<VersionedName> {
public static final VersionedNameComparator INSTANCE = new VersionedNameComparator();
@Override
public int compare(VersionedName o1, VersionedName o2) {
if (o1==null) { return o2==null ? 0 : 1; }
if (o2==null) { return -1; }
return ComparisonChain.start()
.compare(o1.getSymbolicName(), o2.getSymbolicName(), NaturalOrderComparator.INSTANCE)
.compare(o2.getOsgiVersionString(), o1.getOsgiVersionString(), VersionComparator.INSTANCE)
.compare(o2.getVersionString(), o1.getVersionString(), VersionComparator.INSTANCE)
.result();
}
}
// RegisteredTypeNameThenWorstFirstComparator
/** Comparator which puts a:3 < a:1 < b:2 < null */
public static class VersionedNameStringComparator implements Comparator<String> {
public static final VersionedNameStringComparator INSTANCE = new VersionedNameStringComparator();
@Override
public int compare(String s1, String s2) {
return VersionedNameComparator.INSTANCE.compare(parseMaybe(s1, false).orNull(), parseMaybe(s2, false).orNull());
}
}
}