1 /******************************************************************************* 2 * Copyright (c) 2007, 2016 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.pde.api.tools.internal.comparator; 15 16 import java.io.PrintWriter; 17 import java.io.StringWriter; 18 19 import org.eclipse.pde.api.tools.internal.problems.ApiProblemFactory; 20 import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers; 21 import org.eclipse.pde.api.tools.internal.provisional.comparator.DeltaProcessor; 22 import org.eclipse.pde.api.tools.internal.provisional.comparator.DeltaVisitor; 23 import org.eclipse.pde.api.tools.internal.provisional.comparator.IDelta; 24 import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem; 25 import org.eclipse.pde.api.tools.internal.util.Util; 26 27 public class Delta implements IDelta { 28 private static final IDelta[] EMPTY_CHILDREN = new IDelta[0]; 29 private static final int INITIAL_SIZE = 4; 30 31 public static final int RESTRICTIONS_MASK = 0xFFFF; 32 public static final int PREVIOUS_RESTRICTIONS_OFFSET = 16; 33 34 /** 35 * Writes the delta to the given {@link PrintWriter} 36 * 37 * @param delta 38 * @param writer 39 */ print(IDelta delta, PrintWriter writer)40 private static void print(IDelta delta, PrintWriter writer) { 41 writer.print("delta (elementType: "); //$NON-NLS-1$ 42 switch (delta.getElementType()) { 43 case IDelta.FIELD_ELEMENT_TYPE: 44 writer.print("field"); //$NON-NLS-1$ 45 break; 46 case IDelta.ANNOTATION_ELEMENT_TYPE: 47 writer.print("annotation type"); //$NON-NLS-1$ 48 break; 49 case IDelta.CLASS_ELEMENT_TYPE: 50 writer.print("class type"); //$NON-NLS-1$ 51 break; 52 case IDelta.INTERFACE_ELEMENT_TYPE: 53 writer.print("interface type"); //$NON-NLS-1$ 54 break; 55 case IDelta.ENUM_ELEMENT_TYPE: 56 writer.print("enum type"); //$NON-NLS-1$ 57 break; 58 case IDelta.API_COMPONENT_ELEMENT_TYPE: 59 writer.print("API component type"); //$NON-NLS-1$ 60 break; 61 case IDelta.METHOD_ELEMENT_TYPE: 62 writer.print("method"); //$NON-NLS-1$ 63 break; 64 case IDelta.CONSTRUCTOR_ELEMENT_TYPE: 65 writer.print("constructor"); //$NON-NLS-1$ 66 break; 67 case IDelta.API_BASELINE_ELEMENT_TYPE: 68 writer.print("API baseline"); //$NON-NLS-1$ 69 break; 70 default: 71 break; 72 } 73 writer.print(", kind : "); //$NON-NLS-1$ 74 writer.print(delta.getKind()); 75 writer.print(", flags : "); //$NON-NLS-1$ 76 writer.print(delta.getFlags()); 77 writer.print(')'); 78 writer.print('-'); 79 writer.print(Util.getDetail(delta)); 80 } 81 82 private IDelta[] children; 83 private String componentID; 84 private String[] datas; 85 private int deltasCounter; 86 private int elementType; 87 private int flags; 88 private String key; 89 90 private int kind; 91 private int oldModifiers; 92 private int newModifiers; 93 private int restrictions; 94 95 private String typeName; 96 97 /** 98 * Constructor 99 */ Delta()100 public Delta() { 101 // use for root delta 102 } 103 104 /** 105 * Constructor 106 * 107 * @param elementType 108 * @param kind 109 * @param flags 110 * @param restrictions 111 * @param modifiers 112 * @param classFile 113 * @param key 114 * @param data 115 */ Delta(String componentID, int elementType, int kind, int flags, int restrictions, int oldModifiers, int newModifiers, String typeName, String key, String data)116 public Delta(String componentID, int elementType, int kind, int flags, int restrictions, int oldModifiers, int newModifiers, String typeName, String key, String data) { 117 this(componentID, elementType, kind, flags, restrictions, 0, oldModifiers, newModifiers, typeName, key, new String[] { data }); 118 } 119 Delta(String componentID, int elementType, int kind, int flags, int restrictions, int previousRestrictions, int oldModifiers, int newModifiers, String typeName, String key, String[] datas)120 public Delta(String componentID, int elementType, int kind, int flags, int restrictions, int previousRestrictions, int oldModifiers, int newModifiers, String typeName, String key, String[] datas) { 121 this.componentID = componentID; 122 this.elementType = elementType; 123 this.kind = kind; 124 this.flags = flags; 125 this.oldModifiers = oldModifiers; 126 this.newModifiers = newModifiers; 127 this.typeName = typeName == null ? Util.EMPTY_STRING : typeName; 128 this.restrictions = (previousRestrictions & RESTRICTIONS_MASK) << PREVIOUS_RESTRICTIONS_OFFSET | (restrictions & RESTRICTIONS_MASK); 129 this.key = key; 130 this.datas = datas; 131 } 132 133 /** 134 * Constructor 135 * 136 * @param elementType 137 * @param kind 138 * @param flags 139 * @param classFile 140 * @param key 141 * @param data 142 */ Delta(String componentID, int elementType, int kind, int flags, String typeName, String key, String data)143 public Delta(String componentID, int elementType, int kind, int flags, String typeName, String key, String data) { 144 this(componentID, elementType, kind, flags, RestrictionModifiers.NO_RESTRICTIONS, 0, 0, typeName, key, data); 145 } 146 147 @Override accept(DeltaVisitor visitor)148 public void accept(DeltaVisitor visitor) { 149 if (visitor.visit(this)) { 150 if (this.children != null) { 151 for (int i = 0, max = this.deltasCounter; i < max; i++) { 152 IDelta delta = this.children[i]; 153 delta.accept(visitor); 154 } 155 } 156 } 157 visitor.endVisit(this); 158 } 159 160 /** 161 * Adds a child delta to this delta. If the specified delta is 162 * <code>null</code> no work is done. 163 * 164 * @param delta the new child delta 165 */ add(IDelta delta)166 public void add(IDelta delta) { 167 if (delta == null) { 168 return; 169 } 170 if (this.children == null) { 171 this.children = new Delta[INITIAL_SIZE]; 172 this.deltasCounter = 0; 173 } 174 int length = this.children.length; 175 if (this.deltasCounter == length) { 176 System.arraycopy(this.children, 0, (this.children = new IDelta[length * 2]), 0, length); 177 } 178 this.children[this.deltasCounter++] = delta; 179 } 180 181 @Override equals(Object obj)182 public boolean equals(Object obj) { 183 if (this == obj) { 184 return true; 185 } 186 if (obj == null) { 187 return false; 188 } 189 if (!(obj instanceof Delta)) { 190 return false; 191 } 192 Delta other = (Delta) obj; 193 if (this.elementType != other.elementType) { 194 return false; 195 } 196 if (this.flags != other.flags) { 197 return false; 198 } 199 if (this.kind != other.kind) { 200 return false; 201 } 202 if (this.oldModifiers != other.oldModifiers) { 203 return false; 204 } 205 if (this.newModifiers != other.newModifiers) { 206 return false; 207 } 208 if (this.restrictions != other.restrictions) { 209 return false; 210 } 211 if (this.typeName == null) { 212 if (other.typeName != null) { 213 return false; 214 } 215 } else if (!this.typeName.equals(other.typeName)) { 216 return false; 217 } 218 if (this.key == null) { 219 if (other.key != null) { 220 return false; 221 } 222 } else if (!this.key.equals(other.key)) { 223 return false; 224 } 225 if (this.datas == null) { 226 if (other.datas != null) { 227 return false; 228 } 229 } else if (other.datas == null) { 230 return false; 231 } else { 232 if (this.datas.length != other.datas.length) { 233 return false; 234 } 235 for (int i = 0, max = this.datas.length; i < max; i++) { 236 if (!this.datas[i].equals(other.datas[i])) { 237 return false; 238 } 239 } 240 } 241 if (this.componentID == null) { 242 if (other.componentID != null) { 243 return false; 244 } 245 } else if (!this.componentID.equals(other.componentID)) { 246 return false; 247 } 248 return true; 249 } 250 251 @Override getComponentVersionId()252 public String getComponentVersionId() { 253 return this.componentID; 254 } 255 256 @Override getComponentId()257 public String getComponentId() { 258 if (this.componentID == null) { 259 return null; 260 } 261 int index = this.componentID.indexOf(Util.VERSION_SEPARATOR); 262 return this.componentID.substring(0, index); 263 } 264 265 @Override getArguments()266 public String[] getArguments() { 267 if (this.datas == null) { 268 return new String[] { typeName }; 269 } 270 return this.datas; 271 } 272 273 @Override getChildren()274 public IDelta[] getChildren() { 275 if (this.children == null) { 276 return EMPTY_CHILDREN; 277 } 278 int resizeLength = this.deltasCounter; 279 if (resizeLength != this.children.length) { 280 System.arraycopy(this.children, 0, (this.children = new IDelta[resizeLength]), 0, resizeLength); 281 } 282 return this.children; 283 } 284 285 @Override getElementType()286 public int getElementType() { 287 return this.elementType; 288 } 289 290 @Override getFlags()291 public int getFlags() { 292 return this.flags; 293 } 294 295 @Override getKey()296 public String getKey() { 297 return this.key; 298 } 299 300 @Override getKind()301 public int getKind() { 302 return this.kind; 303 } 304 305 @Override getMessage()306 public String getMessage() { 307 if (DeltaProcessor.isCompatible(this)) { 308 return Messages.getCompatibleLocalizedMessage(this); 309 } 310 int id = ApiProblemFactory.getProblemMessageId(IApiProblem.CATEGORY_COMPATIBILITY, this.elementType, this.kind, this.flags); 311 return ApiProblemFactory.getLocalizedMessage(id, (this.datas != null ? this.datas : null)); 312 } 313 314 @Override getNewModifiers()315 public int getNewModifiers() { 316 return this.newModifiers; 317 } 318 319 @Override getOldModifiers()320 public int getOldModifiers() { 321 return this.oldModifiers; 322 } 323 324 @Override getCurrentRestrictions()325 public int getCurrentRestrictions() { 326 return (this.restrictions & RESTRICTIONS_MASK); 327 } 328 329 @Override getPreviousRestrictions()330 public int getPreviousRestrictions() { 331 return (this.restrictions >>> PREVIOUS_RESTRICTIONS_OFFSET); 332 } 333 334 @Override getTypeName()335 public String getTypeName() { 336 return this.typeName; 337 } 338 339 @Override hashCode()340 public int hashCode() { 341 final int prime = 31; 342 int result = 1; 343 result = prime * result + ((this.datas == null) ? 0 : this.datas.hashCode()); 344 result = prime * result + this.elementType; 345 result = prime * result + this.flags; 346 result = prime * result + ((this.key == null) ? 0 : this.key.hashCode()); 347 result = prime * result + ((this.typeName == null) ? 0 : this.typeName.hashCode()); 348 result = prime * result + this.kind; 349 result = prime * result + this.oldModifiers; 350 result = prime * result + this.newModifiers; 351 result = prime * result + this.restrictions; 352 result = prime * result + ((this.componentID == null) ? 0 : this.componentID.hashCode()); 353 return result; 354 } 355 356 @Override isEmpty()357 public boolean isEmpty() { 358 return this.deltasCounter == 0; 359 } 360 361 @Override toString()362 public String toString() { 363 StringWriter writer = new StringWriter(); 364 PrintWriter printWriter = new PrintWriter(writer); 365 if (this.children == null) { 366 print(this, printWriter); 367 } else { 368 printWriter.print('['); 369 for (int i = 0, max = this.deltasCounter; i < max; i++) { 370 if (i > 0) { 371 printWriter.println(','); 372 } 373 printWriter.print(this.children[i]); 374 } 375 printWriter.print(']'); 376 } 377 return String.valueOf(writer.getBuffer()); 378 } 379 } 380