001package io.ebean.dbmigration; 002 003import java.util.Arrays; 004 005/** 006 * The version of a migration used so that migrations are processed in order. 007 */ 008public class MigrationVersion implements Comparable<MigrationVersion> { 009 010 private static final int[] REPEAT_ORDERING = {Integer.MAX_VALUE}; 011 012 private static final boolean[] REPEAT_UNDERSCORES = {false}; 013 014 /** 015 * The raw version text. 016 */ 017 private final String raw; 018 019 /** 020 * The ordering parts. 021 */ 022 private final int[] ordering; 023 024 private final boolean[] underscores; 025 026 private final String comment; 027 028 /** 029 * Construct for "repeatable" version. 030 */ 031 private MigrationVersion(String raw, String comment) { 032 this.raw = raw; 033 this.comment = comment; 034 this.ordering = REPEAT_ORDERING; 035 this.underscores = REPEAT_UNDERSCORES; 036 } 037 038 /** 039 * Construct for "normal" version. 040 */ 041 private MigrationVersion(String raw, int[] ordering, boolean[] underscores, String comment) { 042 this.raw = raw; 043 this.ordering = ordering; 044 this.underscores = underscores; 045 this.comment = comment; 046 } 047 048 /** 049 * Return true if this is a "repeatable" version. 050 */ 051 public boolean isRepeatable() { 052 return ordering == REPEAT_ORDERING; 053 } 054 055 /** 056 * Return the full version. 057 */ 058 public String getFull() { 059 return raw; 060 } 061 062 public String toString() { 063 return raw; 064 } 065 066 /** 067 * Return the version comment. 068 */ 069 public String getComment() { 070 return comment; 071 } 072 073 /** 074 * Return the version in raw form. 075 */ 076 public String getRaw() { 077 return raw; 078 } 079 080 /** 081 * Return the trimmed version excluding version comment and un-parsable string. 082 */ 083 public String asString() { 084 return formattedVersion(false, false); 085 } 086 087 /** 088 * Return the trimmed version with any underscores replaced with '.' 089 */ 090 public String normalised() { 091 return formattedVersion(true, false); 092 } 093 094 /** 095 * Return the next version based on this version. 096 */ 097 public String nextVersion() { 098 return formattedVersion(false, true); 099 } 100 101 /** 102 * Returns the version part of the string. 103 * <p> 104 * Normalised means always use '.' delimiters (no underscores). 105 * NextVersion means bump/increase the last version number by 1. 106 */ 107 private String formattedVersion(boolean normalised, boolean nextVersion) { 108 109 if (ordering == REPEAT_ORDERING) { 110 return "R"; 111 } 112 StringBuilder sb = new StringBuilder(); 113 for (int i = 0; i < ordering.length; i++) { 114 if (i < ordering.length - 1) { 115 sb.append(ordering[i]); 116 if (normalised) { 117 sb.append('.'); 118 } else { 119 sb.append(underscores[i] ? '_' : '.'); 120 } 121 } else { 122 sb.append((nextVersion) ? ordering[i] + 1 : ordering[i]); 123 } 124 } 125 return sb.toString(); 126 } 127 128 @Override 129 public int compareTo(MigrationVersion other) { 130 131 int otherLength = other.ordering.length; 132 for (int i = 0; i < ordering.length; i++) { 133 if (i >= otherLength) { 134 // considered greater 135 return 1; 136 } 137 if (ordering[i] != other.ordering[i]) { 138 return (ordering[i] > other.ordering[i]) ? 1 : -1; 139 } 140 } 141 return comment.compareTo(other.comment); 142 } 143 144 /** 145 * Parse the raw version string and just return the leading version number; 146 */ 147 public static String trim(String raw) { 148 return parse(raw).asString(); 149 } 150 151 /** 152 * Parse the raw version string into a MigrationVersion. 153 */ 154 public static MigrationVersion parse(String raw) { 155 156 if (raw.startsWith("V") || raw.startsWith("v")) { 157 raw = raw.substring(1); 158 } 159 160 String comment = ""; 161 String value = raw; 162 int commentStart = raw.indexOf("__"); 163 if (commentStart > -1) { 164 // trim off the trailing comment 165 comment = raw.substring(commentStart + 2); 166 value = value.substring(0, commentStart); 167 } 168 169 value = value.replace('_', '.'); 170 171 String[] sections = value.split("[\\.-]"); 172 173 if (sections[0].startsWith("R") || sections[0].startsWith("r")) { 174 // a "repeatable" version (does not have a version number) 175 return new MigrationVersion(raw, comment); 176 } 177 178 boolean[] underscores = new boolean[sections.length]; 179 int[] ordering = new int[sections.length]; 180 181 int delimiterPos = 0; 182 int stopIndex = 0; 183 for (int i = 0; i < sections.length; i++) { 184 try { 185 ordering[i] = Integer.parseInt(sections[i]); 186 stopIndex++; 187 188 delimiterPos += sections[i].length(); 189 underscores[i] = (delimiterPos < raw.length() - 1 && raw.charAt(delimiterPos) == '_'); 190 delimiterPos++; 191 } catch (NumberFormatException e) { 192 // stop parsing 193 break; 194 } 195 } 196 197 int[] actualOrder = Arrays.copyOf(ordering, stopIndex); 198 boolean[] actualUnderscores = Arrays.copyOf(underscores, stopIndex); 199 200 return new MigrationVersion(raw, actualOrder, actualUnderscores, comment); 201 } 202 203}