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.patcheval.representation;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import org.apache.logging.log4j.Logger;
  import org.eclipse.steady.shared.enums.ConstructChangeType;
  import org.eclipse.steady.shared.json.model.AffectedConstructChange;
  import org.eclipse.steady.shared.json.model.ConstructChange;
  import org.eclipse.steady.shared.json.model.LibraryId;
  import org.eclipse.steady.shared.json.model.Version;
  
  /**
   * class containing the results on a single archive.
   */
  public class ArtifactResult2 implements Comparable<ArtifactResult2> {
    private static final Logger log = org.apache.logging.log4j.LogManager.getLogger();
  
    String group, artifact;
  
    Version version;
  
    Boolean sourcesAvailable;
  
    List<ConstructPathAssessment2> constructPathAssessments;
  
    Long timestamp;
  
    /**
     * <p>Constructor for ArtifactResult2.</p>
     *
     * @param _g a {@link java.lang.String} object.
     * @param _a a {@link java.lang.String} object.
     * @param _v a {@link java.lang.String} object.
     * @param _s a boolean.
     * @param timestamp a {@link java.lang.Long} object.
     */
    public ArtifactResult2(String _g, String _a, String _v, boolean _s, Long timestamp) {
  
      this.group = _g;
      this.artifact = _a;
      this.version = new Version(_v);
  
      this.timestamp = timestamp;
      this.sourcesAvailable = _s;
      this.constructPathAssessments = new ArrayList<ConstructPathAssessment2>();
    }
  
    /**
     * <p>Constructor for ArtifactResult2.</p>
     *
     * @param _g a {@link java.lang.String} object.
     * @param _a a {@link java.lang.String} object.
     * @param _v a {@link java.lang.String} object.
     * @param _s a boolean.
     */
    public ArtifactResult2(String _g, String _a, String _v, boolean _s) {
  
      this.group = _g;
      this.artifact = _a;
      this.version = new Version(_v);
  
      this.sourcesAvailable = _s;
      this.constructPathAssessments = new ArrayList<ConstructPathAssessment2>();
    }
  
    /**
     * <p>addConstructPathAssessment.</p>
     *
     * @param constructPathAssessment2 a {@link org.eclipse.steady.patcheval.representation.ConstructPathAssessment2} object.
     */
    public void addConstructPathAssessment(ConstructPathAssessment2 constructPathAssessment2) {
      this.constructPathAssessments.add(constructPathAssessment2);
    }
  
    /**
     * <p>Getter for the field <code>constructPathAssessments</code>.</p>
     *
     * @return a {@link java.util.List} object.
     */
    public List<ConstructPathAssessment2> getConstructPathAssessments() {
      return constructPathAssessments;
    }
  
    /**
     * <p>Getter for the field <code>group</code>.</p>
     *
     * @return a {@link java.lang.String} object.
     */
    public String getGroup() {
      return group;
    }
  
    /**
     * <p>Setter for the field <code>group</code>.</p>
     *
     * @param group a {@link java.lang.String} object.
     */
    public void setGroup(String group) {
      this.group = group;
    }
  
    /**
     * <p>Getter for the field <code>artifact</code>.</p>
     *
     * @return a {@link java.lang.String} object.
     */
    public String getArtifact() {
      return artifact;
    }
  
    /**
     * <p>Setter for the field <code>artifact</code>.</p>
     *
     * @param artifact a {@link java.lang.String} object.
     */
    public void setArtifact(String artifact) {
      this.artifact = artifact;
    }
  
    /**
     * <p>Getter for the field <code>version</code>.</p>
     *
     * @return a {@link java.lang.String} object.
     */
    public String getVersion() {
      return version.getVersion();
    }
  
    /**
     * <p>getVersionObject.</p>
     *
     * @return a {@link org.eclipse.steady.shared.json.model.Version} object.
     */
    public Version getVersionObject() {
      return version;
    }
  
    /**
     * <p>Setter for the field <code>version</code>.</p>
     *
     * @param version a {@link org.eclipse.steady.shared.json.model.Version} object.
     */
    public void setVersion(Version version) {
      this.version = version;
    }
  
    /**
     * <p>getSourceAvailable.</p>
     *
     * @return a {@link java.lang.Boolean} object.
     */
    public Boolean getSourceAvailable() {
      return sourcesAvailable;
    }
  
    /**
     * <p>setSourceAvailable.</p>
     *
     * @param s a {@link java.lang.Boolean} object.
     */
    public void setSourceAvailable(Boolean s) {
      this.sourcesAvailable = s;
    }
  
    /**
     * <p>getMajorRelease.</p>
     *
     * @return a {@link java.lang.String} object.
     */
    public String getMajorRelease() {
      return this.version.getMajorRelease();
      //    	String[] versions = this.version.split("\\.");
      //        return (versions.length>0)?versions[0]:"0";
  
    }
  
    /**
     * <p>getMinorRelease.</p>
     *
     * @return a {@link java.lang.String} object.
     */
    public String getMinorRelease() {
      return this.version.getMinorRelease();
      //    	 String[] versions = this.version.split("\\.");
      //         String majorRelease=(versions.length>0)?versions[0]:"0";
      //         //check whether the minor starts with numbers: if it contains also letter just
      // consider the numbers
      //
      //         if(versions.length>1){
      //        	 Pattern p = Pattern.compile("^[0-9]*");
      //        	 Matcher m = p.matcher(versions[1]);
      //        	 if(m.find() ){
      //        		 return majorRelease.concat(".".concat(m.group()));
      //			}
      //         }
      //         return majorRelease.concat(".0");
    }
  
    /**
     * <p>getMaintenanceRelease.</p>
     *
     * @return a {@link java.lang.String} object.
     */
    public String getMaintenanceRelease() {
      return this.version.getMaintenanceRelease();
      //    	 String[] versions = this.version.split("\\.");
      //         String majorRelease=(versions.length>0)?versions[0]:"0";
      //         String minorRelease =
      // majorRelease.concat(".".concat((versions.length>1)?versions[1]:"0"));
      //         return minorRelease.concat(".".concat((versions.length>2)?versions[2]:"0"));
      //
    }
  
    //    public Boolean isMicro(){
    //    	if (this.version.split("\\.").length>3)
    //    		return true;
    //    	else
    //    		return false;
    //    }
  
    /**
     * <p>getVConfidence.</p>
     *
     * @return a {@link java.lang.Double} object.
     */
    public Double getVConfidence() {
      int tot = 0, totV = 0;
      for (ConstructPathAssessment2 cpa : this.getConstructPathAssessments()) {
        if (cpa.getType().equals("MOD")) {
          tot++;
          if (cpa.dToV == 0) totV++;
          else
            ArtifactResult2.log.debug(
                "Construct [" + cpa.getConstruct() + "] not equal to Vulnerable");
        }
      }
      ArtifactResult2.log.debug(
          "Archive ["
              + this.toString()
              + "] is vulnerable for ["
              + totV
              + "] constructs over a total of ["
              + tot
              + "]");
      if (tot != 0) return totV / (double) tot;
      else return new Double(0);
    }
  
    /**
     * <p>getVPathConfidence.</p>
     *
     * @return a {@link java.lang.Double} object.
     */
    public Double getVPathConfidence() {
      int tot = 0, totV = 0;
      Double confidence = new Double(0);
      List<String> processedPath = new ArrayList<String>();
      for (int i = 0; i < this.getConstructPathAssessments().size(); i++) {
        ConstructPathAssessment2 cpa = this.getConstructPathAssessments().get(i);
        String path = cpa.getPath();
        if (!processedPath.contains(path) && cpa.getType().equals("MOD")) {
          processedPath.add(path);
          tot = 1;
          totV = (cpa.dToV == 0) ? 1 : 0;
          for (int j = i + 1; j < this.getConstructPathAssessments().size(); j++) {
            ConstructPathAssessment2 cpa2 = this.getConstructPathAssessments().get(j);
            if (cpa2.getType().equals("MOD") && cpa2.getPath().equals(path)) {
              tot++;
              if (cpa2.dToV == 0) totV++;
              //		    			else
              //		    				ArtifactResult2.log.debug("Construct [" + cpa2.getConstruct() + "] not equal
              // to Vulnerable");
            }
          }
          ArtifactResult2.log.debug(
              "Archive ["
                  + this.toString()
                  + "] is vulnerable for ["
                  + totV
                  + "] constructs over a total of ["
                  + tot
                  + "]");
          Double c = totV / (double) tot;
          if (c > confidence) confidence = c;
        }
      }
      return confidence;
    }
  
    /**
     * <p>getFConfidence.</p>
     *
     * @return a {@link java.lang.Double} object.
     */
    public Double getFConfidence() {
      int tot = 0, totF = 0;
      for (ConstructPathAssessment2 cpa : this.getConstructPathAssessments()) {
        if (cpa.getType().equals("MOD")) {
          tot++;
          if (cpa.dToF == 0) totF++;
          else ArtifactResult2.log.debug("Construct [" + cpa.getConstruct() + "] not equal to Fixed");
        }
      }
      ArtifactResult2.log.debug(
          "Archive ["
              + this.toString()
              + "] is fixed for ["
              + totF
              + "] over a total of ["
              + tot
              + "]");
      if (tot != 0) return totF / (double) tot;
      else return new Double(0);
    }
  
    /**
     * <p>getFPathConfidence.</p>
     *
     * @return a {@link java.lang.Double} object.
     */
    public Double getFPathConfidence() {
      int tot = 0, totF = 0;
      Double confidence = new Double(0);
      List<String> processedPath = new ArrayList<String>();
      for (int i = 0; i < this.getConstructPathAssessments().size(); i++) {
        ConstructPathAssessment2 cpa = this.getConstructPathAssessments().get(i);
        String path = cpa.getPath();
        if (!processedPath.contains(path) && cpa.getType().equals("MOD")) {
          processedPath.add(path);
          tot = 1;
          totF = (cpa.dToF == 0) ? 1 : 0;
          for (int j = i + 1; j < this.getConstructPathAssessments().size(); j++) {
            ConstructPathAssessment2 cpa2 = this.getConstructPathAssessments().get(j);
            if (cpa2.getType().equals("MOD") && cpa2.getPath().equals(path)) {
              tot++;
              if (cpa2.dToF == 0) totF++;
              //		    			else
              //		    				ArtifactResult2.log.debug("Construct [" + cpa2.getConstruct() + "] not equal
              // to Vulnerable");
            }
          }
          ArtifactResult2.log.debug(
              "Archive ["
                  + this.toString()
                  + "] is vulnerable for ["
                  + totF
                  + "] constructs over a total of ["
                  + tot
                  + "] for path ["
                  + path
                  + "]");
          Double c = totF / (double) tot;
          if (c > confidence) confidence = c;
        }
      }
  
      return confidence;
    }
  
    /**
     * <p>Getter for the field <code>timestamp</code>.</p>
     *
     * @return a {@link java.lang.Long} object.
     */
    public Long getTimestamp() {
      return timestamp;
    }
  
    /**
     * <p>Setter for the field <code>timestamp</code>.</p>
     *
     * @param timestamp a long.
     */
    public void setTimestamp(long timestamp) {
      this.timestamp = timestamp;
    }
  
    //    public String getMajorRelease() {
    //        return majorRelease;
    //    }
    //
    //    public void setMajorRelease(String majorRelease) {
    //        this.majorRelease = majorRelease;
    //    }
  
    private boolean constructResultsConsistent() {
      boolean foundV = false;
      boolean foundF = false;
      for (ConstructPathAssessment2 cpa : this.constructPathAssessments) {
        if (cpa.getType().equals("MOD") && cpa.getdToV() == 0 && cpa.getdToF() != 0) foundV = true;
        else if (cpa.getType().equals("MOD") && cpa.getdToV() != 0 && cpa.getdToF() == 0)
          foundF = true;
        else if (cpa.getType().equals("MOD") && cpa.getdToV() == 0 && cpa.getdToF() == 0) {
          // ArtifactResult2.log.warn("Found a construct equal both to VULNERABLE and to FIXED for the
          // same BUG, LIB [" + this.toString()+"]");
          return false;
        }
      }
      if (foundV && foundF) {
        //	ArtifactResult2.log.warn("Found a construct equal to VULNERABLE and another equal to FIXED
        // for the same BUG, LIB [" + this.toString()+"]");
        return false;
      } else return true;
    }
  
    /**
     * <p>containsAD.</p>
     *
     * @return a boolean.
     */
    public boolean containsAD() {
      for (ConstructPathAssessment2 cpa : this.constructPathAssessments) {
        if (cpa.getType().equals("ADD") || cpa.getType().equals("DEL")) return true;
      }
      return false;
    }
  
    // tobe done per path?
    /**
     * <p>isADFixed.</p>
     *
     * @return a boolean.
     */
    public boolean isADFixed() {
      Boolean fixed = false;
      for (ConstructPathAssessment2 cpa : this.constructPathAssessments) {
        if (cpa.getType().equals("ADD") && cpa.getQnameInJar().equals(Boolean.FALSE)) return false;
        else if (cpa.getType().equals("ADD") && cpa.getQnameInJar().equals(Boolean.TRUE))
          fixed = true;
        else if (cpa.getType().equals("DEL") && cpa.getQnameInJar().equals(Boolean.TRUE))
          return false;
        else if (cpa.getType().equals("DEL") && cpa.getQnameInJar().equals(Boolean.FALSE))
          fixed = true;
      }
      return fixed;
    }
  
    /**
     * <p>isPathADFixed.</p>
     *
     * @return a {@link java.lang.Boolean} object.
     */
    public Boolean isPathADFixed() {
      Boolean fixed = false;
      for (ConstructPathAssessment2 cpa2 : this.getConstructPathAssessments()) {
        String path = cpa2.getPath();
        for (ConstructPathAssessment2 cpa : this.getConstructPathAssessments()) {
          if (cpa.getPath().equals(path)) {
            if (cpa.getType().equals("ADD") && cpa.getQnameInJar().equals(Boolean.FALSE)) {
              fixed = false;
              break;
            } else if (cpa.getType().equals("ADD") && cpa.getQnameInJar().equals(Boolean.TRUE))
              fixed = true;
            else if (cpa.getType().equals("DEL") && cpa.getQnameInJar().equals(Boolean.TRUE)) {
              fixed = false;
              break;
            } else if (cpa.getType().equals("DEL") && cpa.getQnameInJar().equals(Boolean.FALSE))
              fixed = true;
          }
        }
        if (fixed == true) return true;
      }
      return false;
    }
  
    /*
     * A LID is equal to vulnerable if at least 1 construct is equal to V
     * and there aren't inconsistencies, i.e., it never happens that equality
     * is found both to V and F across all constructs of the change list
     */
    /**
     * <p>isEqualToV.</p>
     *
     * @return a boolean.
     */
    public boolean isEqualToV() {
      if (constructResultsConsistent()) {
        for (ConstructPathAssessment2 cpa : this.constructPathAssessments) {
          if (cpa.getType().equals("MOD") && cpa.getdToV() == 0) return true;
        }
      }
      return false;
    }
  
    /*
     * A LID is equal to fixed if at least 1 construct is equal to F
     * and there aren't inconsistencies, i.e., it never happens that equality
     * if found both to V and F across all constructs of the change list
     */
  
    /**
     * <p>isEqualToF.</p>
     *
     * @return a boolean.
     */
    public boolean isEqualToF() {
      if (constructResultsConsistent()) {
        for (ConstructPathAssessment2 cpa : this.constructPathAssessments) {
          if (cpa.getType().equals("MOD") && cpa.getdToF() == 0) {
            return true;
          }
        }
      }
      return false;
    }
  
    /* (non-Javadoc)
     *
     * ArtifactResult2 are ordered according to the version of the LibraryId, i.e.,
     * 1) to the numbering schema, if only digits are contained
     * 2) to the timestap (of publication in maven central)
     * 3) alphanumerical comparison
     *  "Note: this class has a natural ordering that is inconsistent with equals."
     *
     * @see java.lang.Comparable#compareTo(java.lang.Object)
     */
    /** {@inheritDoc} */
    @Override
    public int compareTo(ArtifactResult2 other) {
  
      if (this.group.equals(other.getGroup())) {
        if (this.artifact.equals(other.getArtifact())) {
          return this.compareVersion(other);
        } else return this.artifact.compareTo(other.getArtifact());
      } else return this.group.compareTo(other.getGroup());
    }
  
    /**
     *
     *  This method compares the versions of ArtifactResults2 according to the following priorities
     * 1) compare version numbers of the form major.minor[.maintenance[.build]], if only digits are contained
     * 2) compare timestamp (of latest update in maven central)
     * 3) alphanumerical comparison
     *
     * @param other a {@link org.eclipse.steady.patcheval.representation.ArtifactResult2} object.
     * @return a int.
     */
    public int compareVersion(ArtifactResult2 other) {
      int i = this.version.compareNumberVersion(other.getVersionObject());
      if (i == 0 && this.getTimestamp() != null && other.getTimestamp() != null)
        i = this.timestamp.compareTo(other.getTimestamp());
      // if the execution is here it means that at least one of the compared version contains
      // something else than numbers
      // if the version does not only contains numbers, we resort to the timestamp to order them,
      // however if the timestamp is the same we resort to
      // a simple string comparison
      if (i == 0) i = this.version.compareTo(other.getVersionObject());
      return i;
    }
  
    /**
     * <p>equalGAV.</p>
     *
     * @param _group a {@link java.lang.String} object.
     * @param _artifact a {@link java.lang.String} object.
     * @param _version a {@link java.lang.String} object.
     * @return a boolean.
     */
    public boolean equalGAV(String _group, String _artifact, String _version) {
      if (this.group.equals(_group)
          && this.artifact.equals(_artifact)
          && this.version.equals(new Version(_version))) {
        return true;
      } else return false;
    }
  
    /**
     * <p>getGa.</p>
     *
     * @return a {@link java.lang.String} object.
     */
    public String getGa() {
  
      return this.group + ":" + this.artifact;
    }
  
    /** {@inheritDoc} */
    @Override
    public String toString() {
  
      return this.group + ":" + this.artifact + ":" + this.version;
    }
  
    /**
     * <p>getAffectedcc.</p>
     *
     * @param ConstructChange a {@link java.util.List} object.
     * @return a {@link com.google.gson.JsonArray} object.
     */
    public List<AffectedConstructChange> getAffectedcc(List<ConstructChange> ConstructChange) {
      List<AffectedConstructChange> affectedcc = new ArrayList<AffectedConstructChange>();
      for (ConstructPathAssessment2 cpa : this.getConstructPathAssessments()) {
        for (ConstructChange c : ConstructChange) {
          // should enter only once in the if
          if (cpa.getConstruct().equals(c.getConstructId().getQname())
              && c.getRepoPath().contains(cpa.getPath())
              && c.getConstructChangeType().toString().equals(cpa.getType())) {
  
            AffectedConstructChange affcc = new AffectedConstructChange();
  
            affcc.setCc(c);
            affcc.setPathGroup(cpa.getPath());
            affcc.setOverall_chg(cpa.getType());
  
            affcc.setQnameInJar(cpa.getQnameInJar());
  
            if (c.getConstructChangeType().equals(ConstructChangeType.MOD)) {
              affcc.setDtv(cpa.getdToV());
              affcc.setDtf(cpa.getdToF());
  
              if (cpa.getAst() != null) {
                affcc.setTestedBody(cpa.getAst());
                affcc.setVulnBody(cpa.getVulnAst());
                affcc.setFixedBody(cpa.getFixedAst());
              } else if (cpa.getLibsSameBytecode() != null) {
                List<LibraryId> sameBytecodeArray = new ArrayList<LibraryId>();
                for (LibraryId l : cpa.getLibsSameBytecode()) {
                  sameBytecodeArray.add(
                      new LibraryId(l.getMvnGroup(), l.getArtifact(), l.getVersion()));
                }
                affcc.setSameBytecodeLids(sameBytecodeArray);
              }
            }
            affectedcc.add(affcc);
            break;
          }
        }
      }
  
      return affectedcc;
    }
  }
  