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      */
 56 
 57     private final static String legacyOpenJDKVersionPattern = "(jdk([0-9]{1,2}(u[0-9]{1,3})?))";
 58     private final static String legacyHSVersionPattern = "((hs[0-9]{1,2}(\\.[0-9]{1,3})?))";
 59     private final static String legacyBuildPattern = "(-b)([0-9]{2,3})";
 60     private final static String OpenJDKVersionPattern = "(jdk-([0-9]+(\\.[0-9]){0,3}))(\\+)([0-9]+)";
 61 
 62     private final static List<Pattern> tagPatterns = List.of(Pattern.compile(legacyOpenJDKVersionPattern + legacyBuildPattern),
 63                                                              Pattern.compile(legacyHSVersionPattern + legacyBuildPattern),
 64                                                              Pattern.compile(OpenJDKVersionPattern));
 65 
 66     /**
 67      * Attempts to create an OpenJDKTag instance from a general Tag.
 68      *
 69      * This will succeed if the tag follows the OpenJDK tag formatting
 70      * conventions.
 71      * @param tag
 72      * @return
 73      */
 74     public static Optional<OpenJDKTag> create(Tag tag) {
 75         for (var pattern : tagPatterns) {
 76             var matcher = pattern.matcher(tag.name());
 77             if (matcher.matches()) {
 78                 return Optional.of(new OpenJDKTag(tag, matcher.group(1), matcher.group(2), matcher.group(4), matcher.group(5)));
 79             }
 80         }
 81 
 82         return Optional.empty();
 83     }
 84 
 85     /**
 86      * The original Tag this OpenJDKTag was created from.
 87      *
 88      * @return
 89      */
 90     public Tag tag() {
 91         return tag;
 92     }
 93 
 94     /**
 95      * Version number, such as 11, 9.1, 8, 7u20.
 96      *
 97      * @return
 98      */
 99     public String version() {
100         return version;
101     }
102 
103     /**
104      * Build number.
105      *
106      * @return
107      */
108     public int buildNum() {
109         return Integer.parseInt(buildNum);
110     }
111 
112     /**
113      * Tag of the previous build (if any).
114      *
115      * @return
116      */
117     public Optional<OpenJDKTag> previous() {
118         if (buildNum() == 0) {
119             return Optional.empty();
120         }
121 
122         // Make sure build numbers < 10 for JDK 9 tags are not prefixed with '0'
123         var previousBuildNum = buildNum() - 1;
124         var formattedBuildNum = String.format(buildPrefix.equals("+") ? "%d" : "%02d", previousBuildNum);
125         var tagName = prefix + buildPrefix + formattedBuildNum;
126         var tag = new Tag(tagName);
127         return create(tag);
128     }
129 
130     @Override
131     public boolean equals(Object o) {
132         if (this == o) {
133             return true;
134         }
135         if (o == null || getClass() != o.getClass()) {
136             return false;
137         }
138         OpenJDKTag that = (OpenJDKTag) o;
139         return tag.equals(that.tag) &&
140                 prefix.equals(that.prefix) &&
141                 version.equals(that.version) &&
142                 buildPrefix.equals(that.buildPrefix) &&
143                 buildNum.equals(that.buildNum);
144     }
145 
146     @Override
147     public int hashCode() {
148         return Objects.hash(tag, prefix, version, buildPrefix, buildNum);
149     }
150 }