001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube 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 * SonarQube 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 License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020package org.sonar.api.database.model;
021
022import org.apache.commons.lang.StringUtils;
023import org.apache.commons.lang.builder.EqualsBuilder;
024import org.apache.commons.lang.builder.HashCodeBuilder;
025import org.apache.commons.lang.builder.ToStringBuilder;
026import org.hibernate.annotations.BatchSize;
027import org.sonar.api.database.BaseIdentifiable;
028import org.sonar.api.resources.Language;
029import org.sonar.api.resources.ProjectLink;
030import org.sonar.api.resources.Resource;
031
032import javax.annotation.Nullable;
033import javax.persistence.CascadeType;
034import javax.persistence.Column;
035import javax.persistence.Entity;
036import javax.persistence.FetchType;
037import javax.persistence.OneToMany;
038import javax.persistence.Table;
039import javax.persistence.Temporal;
040import javax.persistence.TemporalType;
041
042import java.util.ArrayList;
043import java.util.Date;
044import java.util.List;
045
046/**
047 * Class to map resource with hibernate model
048 */
049@Entity
050@Table(name = "projects")
051public class ResourceModel extends BaseIdentifiable implements Cloneable {
052
053  public static final String SCOPE_PROJECT = "PRJ";
054  public static final String QUALIFIER_PROJECT_TRUNK = "TRK";
055
056  public static final int DESCRIPTION_COLUMN_SIZE = 2000;
057  public static final int NAME_COLUMN_SIZE = 256;
058  public static final int KEY_SIZE = 400;
059  public static final int PATH_SIZE = 2000;
060
061  @Column(name = "name", updatable = true, nullable = true, length = NAME_COLUMN_SIZE)
062  private String name;
063
064  @Column(name = "long_name", updatable = true, nullable = true, length = NAME_COLUMN_SIZE)
065  private String longName;
066
067  @Column(name = "description", updatable = true, nullable = true, length = DESCRIPTION_COLUMN_SIZE)
068  private String description;
069
070  @Column(name = "enabled", updatable = true, nullable = false)
071  private Boolean enabled = Boolean.TRUE;
072
073  @Column(name = "scope", updatable = true, nullable = false, length = 3)
074  private String scope;
075
076  @Column(name = "qualifier", updatable = true, nullable = false, length = 10)
077  private String qualifier;
078
079  @Column(name = "kee", updatable = true, nullable = false, length = KEY_SIZE)
080  private String key;
081
082  @Column(name = "deprecated_kee", updatable = true, nullable = true, length = KEY_SIZE)
083  private String deprecatedKey;
084
085  @Column(name = "language", updatable = true, nullable = true, length = 20)
086  private String languageKey;
087
088  @Column(name = "root_id", updatable = true, nullable = true)
089  private Integer rootId;
090
091  @Column(name = "path", updatable = true, nullable = true, length = PATH_SIZE)
092  private String path;
093
094  @Column(name = "copy_resource_id", updatable = true, nullable = true)
095  private Integer copyResourceId;
096
097  @Column(name = "person_id", updatable = true, nullable = true)
098  private Integer personId;
099
100  @Temporal(TemporalType.TIMESTAMP)
101  @Column(name = "created_at", updatable = true, nullable = true)
102  private Date createdAt;
103
104  @OneToMany(mappedBy = "resource", fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE})
105  @BatchSize(size = 8)
106  private List<ProjectLink> projectLinks = new ArrayList<ProjectLink>();
107
108  /**
109   * Default constructor
110   */
111  public ResourceModel() {
112    this.createdAt = new Date();
113  }
114
115  public ResourceModel(String scope, String key, String qualifier, Integer rootId, String name) {
116    this(scope, key, qualifier, rootId, null, name);
117  }
118
119  /**
120   * <p>Creates a resource model</p>
121   *
122   * @param scope     the scope the rule will apply on
123   * @param key       the rule key. This is the name of the resource, including the path
124   * @param qualifier the resource qualifier
125   * @param rootId    the rootId for the resource
126   * @param path      the path of the resource
127   * @param name      the short name of the resource
128   */
129  public ResourceModel(String scope, String key, String qualifier, Integer rootId, @Nullable String path, String name) {
130    // call this to have the "createdAt" field initialized
131    this();
132    this.scope = scope;
133    this.key = key;
134    this.rootId = rootId;
135    this.path = path;
136    this.name = name;
137    this.qualifier = qualifier;
138  }
139
140  /**
141   * Only available at project level.
142   */
143  public List<ProjectLink> getProjectLinks() {
144    return projectLinks;
145  }
146
147  public void setProjectLinks(List<ProjectLink> projectLinks) {
148    this.projectLinks = projectLinks;
149  }
150
151  /**
152   * @return a project link given its key if exists, null otherwise
153   */
154  public ProjectLink getProjectLink(String key) {
155    for (ProjectLink projectLink : projectLinks) {
156      if (key.equals(projectLink.getKey())) {
157        return projectLink;
158      }
159    }
160    return null;
161  }
162
163  /**
164   * Only available at project level.
165   */
166  public String getDescription() {
167    return description;
168  }
169
170  /**
171   * Sets the resource description, truncated to DESCRIPTION_COLUMN_SIZE
172   */
173  public void setDescription(String description) {
174    this.description = StringUtils.abbreviate(description, DESCRIPTION_COLUMN_SIZE);
175  }
176
177  public String getName() {
178    return name;
179  }
180
181  /**
182   * Sets the resource name, truncated to NAME_COLUMN_SIZE
183   */
184  public void setName(String name) {
185    this.name = StringUtils.abbreviate(name, NAME_COLUMN_SIZE);
186    if (this.longName == null) {
187      this.longName = this.name;
188    }
189  }
190
191  public String getLongName() {
192    return longName;
193  }
194
195  /**
196   * Sets the long name of the resource, truncated to NAME_COLUMN_SIZE
197   */
198  public void setLongName(String s) {
199    if (StringUtils.isBlank(s)) {
200      this.longName = name;
201    } else {
202      this.longName = StringUtils.abbreviate(s, NAME_COLUMN_SIZE);
203    }
204  }
205
206  public Boolean getEnabled() {
207    return enabled;
208  }
209
210  public void setEnabled(Boolean enabled) {
211    this.enabled = enabled;
212  }
213
214  public String getScope() {
215    return scope;
216  }
217
218  public void setScope(String scope) {
219    this.scope = scope;
220  }
221
222  public String getKey() {
223    return key;
224  }
225
226  public String getDeprecatedKey() {
227    return deprecatedKey;
228  }
229
230  public String getLanguageKey() {
231    return languageKey;
232  }
233
234  public void setLanguageKey(String lang) {
235    this.languageKey = lang;
236  }
237
238  public Integer getCopyResourceId() {
239    return copyResourceId;
240  }
241
242  public void setCopyResourceId(Integer i) {
243    this.copyResourceId = i;
244  }
245
246  /**
247   * @since 2.14
248   */
249  public Integer getPersonId() {
250    return personId;
251  }
252
253  /**
254   * @since 2.14
255   */
256  public ResourceModel setPersonId(Integer i) {
257    this.personId = i;
258    return this;
259  }
260
261  /**
262   * @throws IllegalArgumentException if the key is longer than KEY_SIZE
263   */
264  public void setKey(String key) {
265    checkSize(key);
266    this.key = key;
267  }
268
269  private void checkSize(String key) {
270    if (key.length() > KEY_SIZE) {
271      throw new IllegalArgumentException("Resource key is too long, max is " + KEY_SIZE + " characters. Got : " + key);
272    }
273  }
274
275  /**
276   * @throws IllegalArgumentException if the key is longer than KEY_SIZE
277   */
278  public void setDeprecatedKey(String deprecatedKey) {
279    checkSize(deprecatedKey);
280    this.deprecatedKey = deprecatedKey;
281  }
282
283  public Integer getRootId() {
284    return rootId;
285  }
286
287  public void setRootId(Integer rootId) {
288    this.rootId = rootId;
289  }
290
291  public String getPath() {
292    return path;
293  }
294
295  public ResourceModel setPath(@Nullable String path) {
296    if (path != null && path.length() > PATH_SIZE) {
297      throw new IllegalArgumentException("Resource path is too long, max is " + PATH_SIZE + " characters. Got : " + path);
298    }
299    this.path = path;
300    return this;
301  }
302
303  public String getQualifier() {
304    return qualifier;
305  }
306
307  public void setQualifier(String qualifier) {
308    this.qualifier = qualifier;
309  }
310
311  public Date getCreatedAt() {
312    return createdAt; // NOSONAR May expose internal representation by returning reference to mutable object
313  }
314
315  public void setCreatedAt(Date createdAt) {
316    this.createdAt = createdAt; // NOSONAR May expose internal representation by returning reference to mutable object
317  }
318
319  @Override
320  public boolean equals(Object obj) {
321    if (!(obj instanceof ResourceModel)) {
322      return false;
323    }
324    if (this == obj) {
325      return true;
326    }
327    ResourceModel other = (ResourceModel) obj;
328    return new EqualsBuilder()
329      .append(key, other.key)
330      .append(enabled, other.enabled)
331      .append(rootId, other.rootId)
332      .isEquals();
333  }
334
335  @Override
336  public int hashCode() {
337    return new HashCodeBuilder(17, 37)
338      .append(key)
339      .append(enabled)
340      .append(rootId)
341      .toHashCode();
342  }
343
344  @Override
345  public String toString() {
346    return new ToStringBuilder(this)
347      .append("id", getId())
348      .append("key", key)
349      .append("deprecatedKey", deprecatedKey)
350      .append("scope", scope)
351      .append("qualifier", qualifier)
352      .append("name", name)
353      .append("longName", longName)
354      .append("lang", languageKey)
355      .append("enabled", enabled)
356      .append("rootId", rootId)
357      .append("path", path)
358      .append("copyResourceId", copyResourceId)
359      .append("personId", personId)
360      .append("createdAt", createdAt)
361      .toString();
362  }
363
364  @Override
365  public Object clone() {
366    ResourceModel clone = new ResourceModel(getScope(), getKey(), getQualifier(), getRootId(), getPath(), getName());
367    clone.setDescription(getDescription());
368    clone.setDeprecatedKey(getDeprecatedKey());
369    clone.setEnabled(getEnabled());
370    clone.setProjectLinks(getProjectLinks());
371    clone.setLanguageKey(getLanguageKey());
372    clone.setCopyResourceId(getCopyResourceId());
373    clone.setLongName(getLongName());
374    clone.setPersonId(getPersonId());
375    clone.setCreatedAt(getCreatedAt());
376    return clone;
377  }
378
379  /**
380   * Maps a resource to a resource model and returns the resource
381   */
382  public static ResourceModel build(Resource resource) {
383    ResourceModel model = new ResourceModel();
384    model.setEnabled(Boolean.TRUE);
385    model.setDescription(resource.getDescription());
386    model.setKey(resource.getEffectiveKey());
387    model.setPath(resource.getPath());
388    Language lang = resource.getLanguage();
389    if (lang != null) {
390      model.setLanguageKey(lang.getKey());
391    }
392    if (StringUtils.isNotBlank(resource.getName())) {
393      model.setName(resource.getName());
394    } else {
395      model.setName(resource.getKey());
396    }
397    model.setLongName(resource.getLongName());
398    model.setQualifier(resource.getQualifier());
399    model.setScope(resource.getScope());
400    return model;
401  }
402
403}