1 /*
  2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 package org.openjdk.skara.vcs.openjdk;
 24 
 25 import org.openjdk.skara.vcs.Tag;
 26 
 27 import java.util.*;
 28 import java.util.regex.Pattern;
 29 
 30 public class OpenJDKTag {
 31     private final Tag tag;
 32     private final String prefix;
 33     private final String version;
 34     private final String buildPrefix;
 35     private final String buildNum;
 36 
 37     private OpenJDKTag(Tag tag, String prefix, String version, String buildPrefix, String buildNum) {
 38         this.tag = tag;
 39         this.prefix = prefix;
 40         this.version = version;
 41         this.buildPrefix = buildPrefix;
 42         this.buildNum = buildNum;
 43     }
 44 
 45     /**
 46      * The patterns have the following groups:
 47      *
 48      *                prefix   version  update  buildPrefix  buildNum
 49      *                -------  -------  ------  -----------  ------
 50      * jdk-9.1+27  -> jdk-9.1  9.1              +            27
 51      * jdk8-b90    -> jdk8     8                -b           90
 52      * jdk7u40-b20 -> jdk7u40  7u40     u20     -b           29
 53      * hs24-b30    -> hs24     24               -b           30
 54      * hs23.6-b19  -> hs23.6   23.6     .6      -b           19
 55      * 11.1+22     -> 11.1     11.1     .1      +            22
 56      */
 57 
 58     private final static String legacyOpenJDKVersionPattern = "(jdk([0-9]{1,2}(u[0-9]{1,3})?))";
 59     private final static String legacyHSVersionPattern = "((hs[0-9]{1,2}(\\.[0-9]{1,3})?))";
 60     private final static String legacyBuildPattern = "(-b)([0-9]{2,3})";
 61     private final static String OpenJDKVersionPattern = "(jdk-([0-9]+(\\.[0-9]){0,3}))(\\+)([0-9]+)";
 62     private final static String OpenJFXVersionPattern = "((?:jdk-){0,1}([1-9](?:(?:[0-9]*)(\\.(?:0|[1-9][0-9]*)){0,3})))(?:(\\+)([0-9]+)|(-ga))";
 63 
 64     private final static List<Pattern> tagPatterns = List.of(Pattern.compile(legacyOpenJDKVersionPattern + legacyBuildPattern),
 65                                                              Pattern.compile(legacyHSVersionPattern + legacyBuildPattern),
 66                                                              Pattern.compile(OpenJDKVersionPattern),
 67                                                              Pattern.compile(OpenJFXVersionPattern));
 68 
 69     /**
 70      * Attempts to create an OpenJDKTag instance from a general Tag.
 71      *
 72      * This will succeed if the tag follows the OpenJDK tag formatting
 73      * conventions.
 74      * @param tag
 75      * @return
 76      */
 77     public static Optional<OpenJDKTag> create(Tag tag) {
 78         for (var pattern : tagPatterns) {
 79             var matcher = pattern.matcher(tag.name());
 80             if (matcher.matches()) {
 81                 return Optional.of(new OpenJDKTag(tag, matcher.group(1), matcher.group(2), matcher.group(4), matcher.group(5)));
 82             }
 83         }
 84 
 85         return Optional.empty();
 86     }
 87 
 88     /**
 89      * The original Tag this OpenJDKTag was created from.
 90      *
 91      * @return
 92      */
 93     public Tag tag() {
 94         return tag;
 95     }
 96 
 97     /**
 98      * Version number, such as 11, 9.1, 8, 7u20.
 99      *
100      * @return
101      */
102     public String version() {
103         return version;
104     }
105 
106     /**
107      * Build number.
108      *
109      * @return
110      */
111     public int buildNum() {
112         if (buildNum == null) {
113             return 0;
114         }
115         return Integer.parseInt(buildNum);
116     }
117 
118     /**
119      * Tag of the previous build (if any).
120      *
121      * @return
122      */
123     public Optional<OpenJDKTag> previous() {
124         if (buildNum() == 0) {
125             return Optional.empty();
126         }
127 
128         // Make sure build numbers < 10 for JDK 9 tags are not prefixed with '0'
129         var previousBuildNum = buildNum() - 1;
130         var formattedBuildNum = String.format(buildPrefix.equals("+") ? "%d" : "%02d", previousBuildNum);
131         var tagName = prefix + buildPrefix + formattedBuildNum;
132         var tag = new Tag(tagName);
133         return create(tag);
134     }
135 
136     @Override
137     public boolean equals(Object o) {
138         if (this == o) {
139             return true;
140         }
141         if (o == null || getClass() != o.getClass()) {
142             return false;
143         }
144         OpenJDKTag that = (OpenJDKTag) o;
145         return tag.equals(that.tag) &&
146                 prefix.equals(that.prefix) &&
147                 version.equals(that.version) &&
148                 buildPrefix.equals(that.buildPrefix) &&
149                 buildNum.equals(that.buildNum);
150     }
151 
152     @Override
153     public int hashCode() {
154         return Objects.hash(tag, prefix, version, buildPrefix, buildNum);
155     }
156 }