code: /**
   * This file is part of Eclipse Steady.
   *
   * 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.
   *
   * SPDX-License-Identifier: Apache-2.0
   * SPDX-FileCopyrightText: Copyright (c) 2018-2020 SAP SE or an SAP affiliate company and Eclipse Steady contributors
   */
  package org.eclipse.steady.java.sign;
  
  import java.io.Serializable;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import org.eclipse.steady.java.sign.gson.ASTSignatureChangeSerializer;
  import org.eclipse.steady.shared.json.JacksonUtil;
  import org.eclipse.steady.sign.Signature;
  import org.eclipse.steady.sign.SignatureChange;
  
  import com.fasterxml.jackson.databind.ser.std.StdSerializer;
  
  import ch.uzh.ifi.seal.changedistiller.distilling.Distiller;
  import ch.uzh.ifi.seal.changedistiller.distilling.DistillerFactory;
  import ch.uzh.ifi.seal.changedistiller.model.classifiers.java.JavaEntityType;
  import ch.uzh.ifi.seal.changedistiller.model.entities.SourceCodeChange;
  import ch.uzh.ifi.seal.changedistiller.model.entities.StructureEntityVersion;
  import ch.uzh.ifi.seal.changedistiller.treedifferencing.Node;
  
  /**
   * <p>ASTSignatureChange class.</p>
   */
  public class ASTSignatureChange extends DistillerUtil implements SignatureChange, Serializable {
  
    public static enum OperationType {
      Insert,
      Update,
      Move,
      Delete
    };
  
    private Signature mDefSignature = null;
  
    private Signature mFixSignature = null;
  
    private Set<SourceCodeChange> listOfChanges = null;
  
    private String mMethodName;
  
    // TODO : (Something fishy here, needs an improvement - see the sourcecode of changedistiller)
    // A StructureEntityVersion has all the SourceCodeChange's
    private StructureEntityVersion structureEntity = null;
  
    /**
     * <p>Constructor for ASTSignatureChange.</p>
     *
     * @param defSignature a {@link org.eclipse.steady.sign.Signature} object.
     * @param fixSignature a {@link org.eclipse.steady.sign.Signature} object.
     */
    public ASTSignatureChange(Signature defSignature, Signature fixSignature) {
      super();
      this.mDefSignature = defSignature;
      this.mFixSignature = fixSignature;
      // Name of the root of the AST for either the fixed or for the defective AST
      mMethodName = ((ASTSignature) mFixSignature).getValue();
    }
  
    /**
     * Used to create instance of ASTSignatureChange during deserialization of
     * JSON object,  when deserializing a ASTSignatureChange, we only have the list of
     * SourceCodeChanges
     * read from the DB.
     *
     * @param srcCodeChanges - List of SourceCodeChanges
     */
    /*public ASTSignatureChange(Set<SourceCodeChange> srcCodeChanges){
    	this(null,null);
    	this.setListOfChanges(srcCodeChanges);
    	List<SourceCodeChange> srcCodeChgs = new ArrayList<SourceCodeChange>(srcCodeChanges);
    	this.structureEntity.setSourceCodeChanges(srcCodeChgs);
    }*/
  
    /**
     * <p>Constructor for ASTSignatureChange.</p>
     *
     * @param strEntityVersion a {@link ch.uzh.ifi.seal.changedistiller.model.entities.StructureEntityVersion} object.
     */
    public ASTSignatureChange(StructureEntityVersion strEntityVersion) {
      // this(null,null);
      this.setStructureEntity(strEntityVersion);
      this.setListOfChanges(
          new HashSet<SourceCodeChange>(this.getStructureEntity().getSourceCodeChanges()));
    }
  
    /**
     * <p>addChange.</p>
     *
     * @param scc a {@link ch.uzh.ifi.seal.changedistiller.model.entities.SourceCodeChange} object.
     */
    public void addChange(SourceCodeChange scc) {
      this.listOfChanges.add(scc);
    }
  
    /**
     * <p>removeChange.</p>
     *
     * @param scc a {@link ch.uzh.ifi.seal.changedistiller.model.entities.SourceCodeChange} object.
     */
    public void removeChange(SourceCodeChange scc) {
      this.listOfChanges.remove(scc);
    }
  
    /**
     * <p>toSourceCodeChanges.</p>
     *
     * @param _objects a {@link java.util.Set} object.
     * @return a {@link java.util.Set} object.
     */
    public static Set<SourceCodeChange> toSourceCodeChanges(Set<Object> _objects) {
      final Set<SourceCodeChange> changes = new HashSet<SourceCodeChange>();
      for (Object o : _objects) {
        changes.add((SourceCodeChange) o);
      }
      return changes;
    }
  
    /**
     * <p>Getter for the field <code>structureEntity</code>.</p>
     *
     * @return a {@link ch.uzh.ifi.seal.changedistiller.model.entities.StructureEntityVersion} object.
     */
    public StructureEntityVersion getStructureEntity() {
      return structureEntity;
    }
  
    /**
     * <p>Setter for the field <code>structureEntity</code>.</p>
     *
     * @param structureEntity a {@link ch.uzh.ifi.seal.changedistiller.model.entities.StructureEntityVersion} object.
     */
    public void setStructureEntity(StructureEntityVersion structureEntity) {
      this.structureEntity = structureEntity;
    }
  
    /** {@inheritDoc} */
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + ((listOfChanges == null) ? 0 : listOfChanges.hashCode());
      return result;
    }
  
    /** {@inheritDoc} */
    @Override
    public boolean equals(Object obj) {
      if (this == obj) return true;
      if (obj == null) return false;
      if (getClass() != obj.getClass()) return false;
      ASTSignatureChange other = (ASTSignatureChange) obj;
      if (listOfChanges == null) {
        if (other.listOfChanges != null) return false;
      } else {
        final boolean is_equal =
            ASTSignatureChange.isSameElements(
                (Collection<SourceCodeChange>) this.listOfChanges,
                (Collection<SourceCodeChange>) other.listOfChanges);
        return is_equal;
      }
      return true;
    }
  
    /**
     * <p>isSameElements.</p>
     *
     * @param _a a {@link java.util.Collection} object.
     * @param _b a {@link java.util.Collection} object.
     * @return a boolean.
     */
    public static boolean isSameElements(
        Collection<SourceCodeChange> _a, Collection<SourceCodeChange> _b) {
      if (_a.size() != _b.size()) return false;
      for (SourceCodeChange this_scc : _a) {
        boolean scc_found = false;
        for (SourceCodeChange other_scc : _b) {
          if (this_scc.equals(other_scc)) {
            scc_found = true;
            break;
          }
        }
        if (!scc_found) return false;
      }
      return true;
    }
  
    /**
     * <p>Getter for the field <code>mDefSignature</code>.</p>
     *
     * @return a {@link org.eclipse.steady.sign.Signature} object.
     */
    public Signature getmDefSignature() {
      return mDefSignature;
    }
  
    /**
     * <p>Setter for the field <code>mDefSignature</code>.</p>
     *
     * @param mDefSignature a {@link org.eclipse.steady.sign.Signature} object.
     */
    public void setmDefSignature(Signature mDefSignature) {
      this.mDefSignature = mDefSignature;
    }
  
    /**
     * <p>Getter for the field <code>mFixSignature</code>.</p>
     *
     * @return a {@link org.eclipse.steady.sign.Signature} object.
     */
    public Signature getmFixSignature() {
      return mFixSignature;
    }
  
    /**
     * <p>Setter for the field <code>mFixSignature</code>.</p>
     *
     * @param mFixSignature a {@link org.eclipse.steady.sign.Signature} object.
     */
    public void setmFixSignature(Signature mFixSignature) {
      this.mFixSignature = mFixSignature;
    }
  
    /**
     * <p>Getter for the field <code>listOfChanges</code>.</p>
     *
     * @return a {@link java.util.Set} object.
     */
    public Set<SourceCodeChange> getListOfChanges() {
      return listOfChanges;
    }
  
    /**
     * <p>Setter for the field <code>listOfChanges</code>.</p>
     *
     * @param listOfChanges a {@link java.util.Set} object.
     */
    public void setListOfChanges(Set<SourceCodeChange> listOfChanges) {
      this.listOfChanges = listOfChanges;
    }
  
    /**
     * <p>Getter for the field <code>mMethodName</code>.</p>
     *
     * @return a {@link java.lang.String} object.
     */
    public String getmMethodName() {
      return mMethodName;
    }
  
    /**
     * <p>Setter for the field <code>mMethodName</code>.</p>
     *
     * @param mMethodName a {@link java.lang.String} object.
     */
    public void setmMethodName(String mMethodName) {
      this.mMethodName = mMethodName;
    }
  
    /**
     * <p>operationTypetoJSON.</p>
     *
     * @param t a {@link org.eclipse.steady.java.sign.ASTSignatureChange.OperationType} object.
     * @return a {@link java.lang.String} object.
     */
    public String operationTypetoJSON(OperationType t) {
      final StringBuilder b = new StringBuilder();
      b.append("\"operationType\" : \"");
      switch (t) {
        case Insert:
          b.append("INSERT\"");
          break;
        case Update:
          b.append("UPDATE\"");
          break;
        case Move:
          b.append("MOVE\"");
          break;
        case Delete:
          b.append("DELETE\"");
          break;
        default:
          b.append("???\"");
          break;
      }
      return b.toString();
    }
  
    private String operationTypeToString(OperationType t) {
      switch (t) {
        case Insert:
          return "I";
        case Update:
          return "U";
        case Move:
          return "M";
        case Delete:
          return "D";
        default:
          return "?";
      }
    }
  
    /**
     * {@inheritDoc}
     *
     * Returns the set of edit operations required to change the defective AST int the fixed one.
     * Attention: This method will change the defective signature passed as argument to the constructor.
     */
    @Override
    public Set<Object> getModifications() {
  
      if (this.listOfChanges == null) {
        structureEntity = new StructureEntityVersion(JavaEntityType.METHOD, this.mMethodName, 0);
        Distiller mDistiller = mInjector.getInstance(DistillerFactory.class).create(structureEntity);
  
        if (this.mDefSignature != null && this.mFixSignature != null) {
          Node defSignatureNode = ((ASTSignature) this.mDefSignature).getRoot();
          Node fixSignatureNode = ((ASTSignature) this.mFixSignature).getRoot();
  
          // This call modifies the "Defective" Version, basically it converts it into the Fixed
          // Version
          // (I don't know why , nothing is mentioned in the API documentation)
          // TODO : Read the the changedistiller paper  for a better understanding
          // (http://www.merlin.uzh.ch/publication/show/2531)
          mDistiller.extractClassifiedSourceCodeChanges(defSignatureNode, fixSignatureNode);
          List<SourceCodeChange> change =
              structureEntity.getSourceCodeChanges(); // Extract the set of sourceCodeChanges from the
          // StructureEntityVersion
  
          // Debug
          if (change != null) this.listOfChanges = new HashSet<SourceCodeChange>(change);
  
          /*	if(change != null)
          {
                for(SourceCodeChange ch : change){
                		listOfChanges.add(ch);
                	}
          }*/
        }
      }
  
      // Good to know :
      // http://stackoverflow.com/questions/905964/how-to-cast-generic-list-types-in-java?answertab=active#tab-top
      final Set<Object> set = new HashSet<Object>();
      if (this.listOfChanges != null) {
        set.addAll(this.listOfChanges); // Replaces the following loop
        /*for (SourceCodeChange  e :  this.listOfChanges) {
        	set.add( e); // need to cast each object specifically
        }*/
      }
      return set;
    }
  
    /** {@inheritDoc} */
    @Override
    public String toJSON() {
      final Map<Class<?>, StdSerializer<?>> custom_serializers =
          new HashMap<Class<?>, StdSerializer<?>>();
      custom_serializers.put(ASTSignatureChange.class, new ASTSignatureChangeSerializer());
      return JacksonUtil.asJsonString(this, custom_serializers);
    }
  
    /**
     * Returns a formatted 'SourceCodeChange' elements
     *
     * @return a {@link java.lang.String} object.
     */
    public String toString() {
      int count = 0;
      final StringBuffer buffer = new StringBuffer();
  
      if (listOfChanges != null) {
        buffer.append("Test - Everything SourceCodeChange Offers :\n");
  
        for (SourceCodeChange change : listOfChanges) {
          buffer.append("\n CHANGE" + ++count + "\n");
          buffer.append("\n ChangedEntity = " + change.getChangedEntity());
          buffer.append(
              "\n ChangedEntity Source Range= " + change.getChangedEntity().getSourceRange());
          buffer.append("\n UniqueName = " + change.getChangedEntity().getUniqueName());
          buffer.append("\n Entity Label = " + change.getChangedEntity().getLabel());
          buffer.append("\n Entity Type = " + change.getChangedEntity().getType().toString());
          buffer.append("\n Change Type Label = " + change.getLabel());
          buffer.append("\n Parent  =  " + change.getParentEntity());
          buffer.append("\n Parent Source Range = " + change.getParentEntity().getSourceRange());
          buffer.append("\n Simple Name = " + change.getClass().getSimpleName());
          buffer.append("\n " + change.toString() + "\n");
          // buffer.append("\n SignificanceLevel(CRUCIAL, HIGH, MEDIMU, LOW) :" +
          // change.getSignificanceLevel().toString());
          // buffer.append("\n ChangedEntity, StartPosition : " +
          // change.getChangedEntity().getStartPosition());
        }
      }
      return buffer.toString();
    }
  
    /** {@inheritDoc} */
    @Override
    public boolean isEmpty() {
      return this.getListOfChanges().size() == 0;
    }
  }
  