001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2009 SonarSource SA
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.api.database.model;
021    
022    import org.apache.commons.lang.builder.EqualsBuilder;
023    import org.apache.commons.lang.builder.HashCodeBuilder;
024    import org.apache.commons.lang.builder.ToStringBuilder;
025    import org.hibernate.annotations.Cache;
026    import org.hibernate.annotations.CacheConcurrencyStrategy;
027    import org.sonar.api.database.BaseIdentifiable;
028    import org.sonar.api.database.DatabaseSession;
029    
030    import java.util.Date;
031    import javax.persistence.*;
032    
033    /**
034     * A class to map a snapshot with its hibernate model
035     */
036    @Entity
037    @Table(name = "snapshots")
038    public class Snapshot extends BaseIdentifiable {
039    
040      /**
041       * This status is set on the snapshot at the beginning of the batch
042       */
043      public final static String STATUS_UNPROCESSED = "U";
044    
045      /**
046       * This status is set on the snapshot at the end of the batch
047       */
048      public final static String STATUS_PROCESSED = "P";
049    
050      @ManyToOne(fetch = FetchType.LAZY)
051      @JoinColumn(name = "project_id", updatable = true, nullable = true)
052      @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
053      private ResourceModel resource;
054    
055      @Temporal(TemporalType.TIMESTAMP)
056      @Column(name = "created_at", updatable = true, nullable = true)
057      private Date createdAt;
058    
059      @Column(name = "version", updatable = true, nullable = true, length = 32)
060      private String version;
061    
062      @Column(name = "islast")
063      private Boolean last = Boolean.FALSE;
064    
065      @Column(name = "status")
066      private String status = STATUS_UNPROCESSED;
067    
068      @Column(name = "scope", updatable = true, nullable = true, length = 3)
069      private String scope;
070    
071      @Column(name = "path", updatable = true, nullable = true, length = 96)
072      private String path;
073    
074      @Column(name = "depth", updatable = true, nullable = true)
075      private Integer depth;
076    
077      @Column(name = "qualifier", updatable = true, nullable = true, length = 3)
078      private String qualifier;
079    
080      @Column(name = "root_snapshot_id", updatable = true, nullable = true)
081      private Integer rootId;
082    
083      @Column(name = "parent_snapshot_id", updatable = true, nullable = true)
084      private Integer parentId;
085    
086      @Column(name = "root_project_id", updatable = true, nullable = true)
087      private Integer rootProjectId;
088    
089      public Snapshot() {
090    
091      }
092    
093      public Snapshot(ResourceModel resource, Snapshot parent) {
094        this.resource = resource;
095    
096        if (resource != null) {
097          this.qualifier = resource.getQualifier();
098          this.scope = resource.getScope();
099        }
100    
101        if (parent == null) {
102          path = "";
103          depth = 0;
104    
105        } else {
106          this.parentId = parent.getId();
107          this.rootId = (parent.getRootId()==null ? parent.getId() : parent.getRootId());
108          this.createdAt = parent.getCreatedAt();
109          this.depth = parent.getDepth() + 1;
110          this.path = new StringBuilder()
111              .append(parent.getPath())
112              .append(parent.getId())
113              .append(".")
114              .toString();
115        }
116        this.rootProjectId = guessRootProjectId(resource, parent);
117      }
118    
119      private static Integer guessRootProjectId(ResourceModel resource, Snapshot parent) {
120        Integer result;
121    
122        // design problem : constants are defined in the Resource class, that should not be used by this class...
123        if ("TRK".equals(resource.getQualifier()) || "VW".equals(resource.getQualifier()) || "SVW".equals(resource.getQualifier())) {
124          result = resource.getCopyResourceId() != null ? resource.getCopyResourceId() : resource.getId();
125    
126        } else if (parent==null) {
127          result = resource.getCopyResourceId() != null ? resource.getCopyResourceId() : resource.getId();
128          
129        } else {
130          result= (parent.getRootProjectId()==null ? parent.getResourceId() : parent.getRootProjectId());
131        }
132        return result;
133      }
134    
135      public Snapshot save(DatabaseSession session) {
136        if (resource != null && resource.getId() != null) {
137          resource = session.reattach(ResourceModel.class, resource.getId());
138        }
139        return session.save(this);
140      }
141    
142      public Snapshot(ResourceModel resource, boolean last, String status, Date date) {
143        this();
144        setResource(resource);
145        this.status = status;
146        this.last = last;
147        this.createdAt = date;
148      }
149    
150      public Date getCreatedAt() {
151        return createdAt;
152      }
153    
154      public void setCreatedAt(Date createdAt) {
155        this.createdAt = createdAt;
156      }
157    
158      public ResourceModel getResource() {
159        return resource;
160      }
161    
162      public Integer getResourceId() {
163        if (resource != null) {
164          return resource.getId();
165        }
166        return null;
167      }
168    
169      public final void setResource(ResourceModel resource) {
170        this.resource = resource;
171        this.scope = resource.getScope();
172        this.qualifier = resource.getQualifier();
173      }
174    
175      public String getVersion() {
176        return version;
177      }
178    
179      public void setVersion(String version) {
180        this.version = version;
181      }
182    
183      public Integer getParentId() {
184        return parentId;
185      }
186    
187      public void setParentId(Integer i) {
188        this.parentId = i;
189      }
190    
191      public Boolean getLast() {
192        return last;
193      }
194    
195      public void setLast(Boolean last) {
196        this.last = last;
197      }
198    
199      public String getStatus() {
200        return status;
201      }
202    
203      public void setStatus(String status) {
204        this.status = status;
205      }
206    
207      public String getScope() {
208        return scope;
209      }
210    
211      public void setScope(String scope) {
212        this.scope = scope;
213      }
214    
215      public String getQualifier() {
216        return qualifier;
217      }
218    
219      public void setQualifier(String qualifier) {
220        this.qualifier = qualifier;
221      }
222    
223      public Integer getRootId() {
224        return rootId;
225      }
226    
227      public void setRootId(Integer i) {
228        this.rootId = i;
229      }
230    
231      public String getPath() {
232        return path;
233      }
234    
235      public void setPath(String path) {
236        this.path = path;
237      }
238    
239      public Integer getDepth() {
240        return depth;
241      }
242    
243      public Integer getRootProjectId() {
244        return rootProjectId;
245      }
246    
247      public void setRootProjectId(Integer rootProjectId) {
248        this.rootProjectId = rootProjectId;
249      }
250    
251      /**
252       * Sets the depth of the snapshot
253       *
254       * @throws IllegalArgumentException when depth is negative
255       */
256      public void setDepth(Integer depth) {
257        if (depth != null && depth < 0) {
258          throw new IllegalArgumentException("depth can not be negative : " + depth);
259        }
260        this.depth = depth;
261      }
262    
263    
264      @Override
265      public boolean equals(Object obj) {
266        if (!(obj instanceof Snapshot)) {
267          return false;
268        }
269        if (this == obj) {
270          return true;
271        }
272        Snapshot other = (Snapshot) obj;
273        return new EqualsBuilder()
274            .append(resource, other.getResource())
275            .append(createdAt, other.getCreatedAt())
276            .isEquals();
277      }
278    
279      @Override
280      public int hashCode() {
281        return new HashCodeBuilder(17, 37)
282            .append(resource)
283            .append(createdAt)
284            .toHashCode();
285      }
286    
287      @Override
288      public String toString() {
289        return new ToStringBuilder(this)
290            .append("id", getId())
291            .append("scope", scope)
292            .append("qualifier", qualifier)
293            .append("version", version)
294            .append("last", last)
295            .append("createdAt", createdAt)
296            .append("status", status)
297            .append("path", path)
298            .append("rootId", rootId)
299            .append("rootProjectId", rootProjectId)
300            .append("parentId", parentId)
301            .toString();
302      }
303    }