/* * Copyright (C) 2012 The Android Open Source Project * * 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.android.sdklib.internal.repository.packages; import com.android.annotations.NonNull; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Package multi-part revision number composed of a tuple * (major.minor.micro) and an optional preview revision * (the lack of a preview number indicates it's not a preview * but a final package.) * * @see MajorRevision */ public class FullRevision implements Comparable<FullRevision> { public static final int MISSING_MAJOR_REV = 0; public static final int IMPLICIT_MINOR_REV = 0; public static final int IMPLICIT_MICRO_REV = 0; public static final int NOT_A_PREVIEW = 0; private final static Pattern FULL_REVISION_PATTERN = // 1=major 2=minor 3=micro 4=preview Pattern.compile("\\s*([0-9]+)(?:\\.([0-9]+)(?:\\.([0-9]+))?)?\\s*(?:rc([0-9]+))?\\s*"); private final int mMajor; private final int mMinor; private final int mMicro; private final int mPreview; public FullRevision(int major) { this(major, 0, 0); } public FullRevision(int major, int minor, int micro) { this(major, minor, micro, NOT_A_PREVIEW); } public FullRevision(int major, int minor, int micro, int preview) { mMajor = major; mMinor = minor; mMicro = micro; mPreview = preview; } public int getMajor() { return mMajor; } public int getMinor() { return mMinor; } public int getMicro() { return mMicro; } public boolean isPreview() { return mPreview > NOT_A_PREVIEW; } public int getPreview() { return mPreview; } /** * Parses a string of format "major.minor.micro rcPreview" and returns * a new {@link FullRevision} for it. All the fields except major are * optional. * <p/> * The parsing is equivalent to the pseudo-BNF/regexp: * <pre> * Major/Minor/Micro/Preview := [0-9]+ * Revision := Major ('.' Minor ('.' Micro)? )? \s* ('rc'Preview)? * </pre> * * @param revision A non-null revision to parse. * @return A new non-null {@link FullRevision}. * @throws NumberFormatException if the parsing failed. */ public static @NonNull FullRevision parseRevision(@NonNull String revision) throws NumberFormatException { if (revision == null) { throw new NumberFormatException("revision is <null>"); //$NON-NLS-1$ } Throwable cause = null; try { Matcher m = FULL_REVISION_PATTERN.matcher(revision); if (m != null && m.matches()) { int major = Integer.parseInt(m.group(1)); String s = m.group(2); int minor = s == null ? IMPLICIT_MINOR_REV : Integer.parseInt(s); s = m.group(3); int micro = s == null ? IMPLICIT_MICRO_REV : Integer.parseInt(s); s = m.group(4); int preview = s == null ? NOT_A_PREVIEW : Integer.parseInt(s); return new FullRevision(major, minor, micro, preview); } } catch (Throwable t) { cause = t; } NumberFormatException n = new NumberFormatException( "Invalid full revision: " + revision); //$NON-NLS-1$ n.initCause(cause); throw n; } /** * Returns the version in a fixed format major.minor.micro * with an optional "rc preview#". For example it would * return "18.0.0", "18.1.0" or "18.1.2 rc5". */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(mMajor) .append('.').append(mMinor) .append('.').append(mMicro); if (mPreview != NOT_A_PREVIEW) { sb.append(" rc").append(mPreview); } return sb.toString(); } /** * Returns the version in a dynamic format "major.minor.micro rc#". * This is similar to {@link #toString()} except it omits minor, micro * or preview versions when they are zero. * For example it would return "18 rc1" instead of "18.0.0 rc1", * or "18.1 rc2" instead of "18.1.0 rc2". */ public String toShortString() { StringBuilder sb = new StringBuilder(); sb.append(mMajor); if (mMinor > 0 || mMicro > 0) { sb.append('.').append(mMinor); } if (mMicro > 0) { sb.append('.').append(mMicro); } if (mPreview != NOT_A_PREVIEW) { sb.append(" rc").append(mPreview); } return sb.toString(); } /** * Returns the version number as an integer array, in the form * [major, minor, micro] or [major, minor, micro, preview]. * * This is useful to initialize an instance of * {@code org.apache.tools.ant.util.DeweyDecimal} using a * {@link FullRevision}. * * @param includePreview If true the output will contain 4 fields * to include the preview number (even if 0.) If false the output * will contain only 3 fields (major, minor and micro.) * @return A new int array, never null, with either 3 or 4 fields. */ public int[] toIntArray(boolean includePreview) { int size = includePreview ? 4 : 3; int[] result = new int[size]; result[0] = mMajor; result[1] = mMinor; result[2] = mMicro; if (result.length > 3) { result[3] = mPreview; } return result; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + mMajor; result = prime * result + mMinor; result = prime * result + mMicro; result = prime * result + mPreview; return result; } @Override public boolean equals(Object rhs) { if (this == rhs) { return true; } if (rhs == null) { return false; } if (!(rhs instanceof FullRevision)) { return false; } FullRevision other = (FullRevision) rhs; if (mMajor != other.mMajor) { return false; } if (mMinor != other.mMinor) { return false; } if (mMicro != other.mMicro) { return false; } if (mPreview != other.mPreview) { return false; } return true; } /** * Trivial comparison of a version, e.g 17.1.2 < 18.0.0. * * Note that preview/release candidate are released before their final version, * so "18.0.0 rc1" comes below "18.0.0". The best way to think of it as if the * lack of preview number was "+inf": * "18.1.2 rc5" => "18.1.2.5" so its less than "18.1.2.+INF" but more than "18.1.1.0" * and more than "18.1.2.4" */ @Override public int compareTo(FullRevision rhs) { int delta = mMajor - rhs.mMajor; if (delta != 0) { return delta; } delta = mMinor - rhs.mMinor; if (delta != 0) { return delta; } delta = mMicro - rhs.mMicro; if (delta != 0) { return delta; } int p1 = mPreview == NOT_A_PREVIEW ? Integer.MAX_VALUE : mPreview; int p2 = rhs.mPreview == NOT_A_PREVIEW ? Integer.MAX_VALUE : rhs.mPreview; delta = p1 - p2; return delta; } }