001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019 */
020package org.sonar.duplications.index;
021
022import com.google.common.annotations.Beta;
023import com.google.common.collect.ImmutableList;
024
025import java.util.List;
026
027/**
028 * Groups a set of related {@link ClonePart}s.
029 */
030public class CloneGroup {
031
032  private final ClonePart originPart;
033  private final int cloneLength;
034  private final List<ClonePart> parts;
035
036  /**
037   * Cache for hash code.
038   */
039  private int hash;
040
041  /**
042   * @since 2.14
043   */
044  public static Builder builder() {
045    return new Builder();
046  }
047
048  /**
049   * @since 2.14
050   */
051  public static final class Builder {
052    private ClonePart origin;
053    private int length;
054    private int lengthInUnits;
055    private List<ClonePart> parts;
056
057    public Builder setLength(int length) {
058      this.length = length;
059      return this;
060    }
061
062    public Builder setOrigin(ClonePart origin) {
063      this.origin = origin;
064      return this;
065    }
066
067    public Builder setParts(List<ClonePart> parts) {
068      this.parts = ImmutableList.copyOf(parts);
069      return this;
070    }
071
072    public Builder setLengthInUnits(int length) {
073      this.lengthInUnits = length;
074      return this;
075    }
076
077    public CloneGroup build() {
078      return new CloneGroup(this);
079    }
080  }
081
082  private CloneGroup(Builder builder) {
083    this.cloneLength = builder.length;
084    this.originPart = builder.origin;
085    this.parts = builder.parts;
086    this.length = builder.lengthInUnits;
087  }
088
089  public ClonePart getOriginPart() {
090    return originPart;
091  }
092
093  private int length;
094
095  /**
096   * Length of duplication measured in original units, e.g. for token-based detection - in tokens.
097   *
098   * @since 2.14
099   */
100  @Beta
101  public int getLengthInUnits() {
102    return length;
103  }
104
105  /**
106   * @return clone length in {@link org.sonar.duplications.block.Block}s
107   */
108  public int getCloneUnitLength() {
109    return cloneLength;
110  }
111
112  public List<ClonePart> getCloneParts() {
113    return parts;
114  }
115
116  @Override
117  public String toString() {
118    StringBuilder builder = new StringBuilder();
119    for (ClonePart part : parts) {
120      builder.append(part).append(" - ");
121    }
122    builder.append(cloneLength);
123    return builder.toString();
124  }
125
126  /**
127   * Two groups are equal, if they have same length, same origins and contain same parts in same order.
128   */
129  @Override
130  public boolean equals(Object object) {
131    if (!(object instanceof CloneGroup)) {
132      return false;
133    }
134    CloneGroup another = (CloneGroup) object;
135    if (another.cloneLength != cloneLength || parts.size() != another.parts.size()) {
136      return false;
137    }
138    if (!originPart.equals(another.originPart)) {
139      return false;
140    }
141    boolean result = true;
142    for (int i = 0; i < parts.size(); i++) {
143      result &= another.parts.get(i).equals(parts.get(i));
144    }
145    return result;
146  }
147
148  @Override
149  public int hashCode() {
150    int h = hash;
151    if (h == 0 && cloneLength != 0) {
152      for (ClonePart part : parts) {
153        h = 31 * h + part.hashCode();
154      }
155      h = 31 * h + originPart.hashCode();
156      h = 31 * h + cloneLength;
157      hash = h;
158    }
159    return h;
160  }
161
162}