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     */
020    package org.sonar.duplications.index;
021    
022    import com.google.common.annotations.Beta;
023    import com.google.common.collect.ImmutableList;
024    
025    import java.util.List;
026    
027    /**
028     * Groups a set of related {@link ClonePart}s.
029     */
030    public 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    }