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  @Column(name = "uuid", updatable = false, nullable = true, length = 50)
109  private String uuid;
110
111  @Column(name = "project_uuid", updatable = true, nullable = true, length = 50)
112  private String projectUuid;
113
114  @Column(name = "module_uuid", updatable = true, nullable = true, length = 50)
115  private String moduleUuid;
116
117  @Column(name = "module_uuid_path", updatable = true, nullable = true, length = 4000)
118  private String moduleUuidPath;
119
120  /**
121   * Default constructor
122   */
123  public ResourceModel() {
124    this.createdAt = new Date();
125  }
126
127  public ResourceModel(String scope, String key, String qualifier, Integer rootId, String name) {
128    this(scope, key, qualifier, rootId, null, name);
129  }
130
131  /**
132   * <p>Creates a resource model</p>
133   *
134   * @param scope     the scope the rule will apply on
135   * @param key       the rule key. This is the name of the resource, including the path
136   * @param qualifier the resource qualifier
137   * @param rootId    the rootId for the resource
138   * @param path      the path of the resource
139   * @param name      the short name of the resource
140   */
141  public ResourceModel(String scope, String key, String qualifier, Integer rootId, @Nullable String path, String name) {
142    // call this to have the "createdAt" field initialized
143    this();
144    this.scope = scope;
145    this.key = key;
146    this.rootId = rootId;
147    this.path = path;
148    this.name = name;
149    this.qualifier = qualifier;
150  }
151
152  /**
153   * Only available at project level.
154   */
155  public List<ProjectLink> getProjectLinks() {
156    return projectLinks;
157  }
158
159  public void setProjectLinks(List<ProjectLink> projectLinks) {
160    this.projectLinks = projectLinks;
161  }
162
163  /**
164   * @return a project link given its key if exists, null otherwise
165   */
166  public ProjectLink getProjectLink(String key) {
167    for (ProjectLink projectLink : projectLinks) {
168      if (key.equals(projectLink.getKey())) {
169        return projectLink;
170      }
171    }
172    return null;
173  }
174
175  /**
176   * Only available at project level.
177   */
178  public String getDescription() {
179    return description;
180  }
181
182  /**
183   * Sets the resource description, truncated to DESCRIPTION_COLUMN_SIZE
184   */
185  public void setDescription(String description) {
186    this.description = StringUtils.abbreviate(description, DESCRIPTION_COLUMN_SIZE);
187  }
188
189  public String getName() {
190    return name;
191  }
192
193  /**
194   * Sets the resource name, truncated to NAME_COLUMN_SIZE
195   */
196  public void setName(String name) {
197    this.name = StringUtils.abbreviate(name, NAME_COLUMN_SIZE);
198    if (this.longName == null) {
199      this.longName = this.name;
200    }
201  }
202
203  public String getLongName() {
204    return longName;
205  }
206
207  /**
208   * Sets the long name of the resource, truncated to NAME_COLUMN_SIZE
209   */
210  public void setLongName(String s) {
211    if (StringUtils.isBlank(s)) {
212      this.longName = name;
213    } else {
214      this.longName = StringUtils.abbreviate(s, NAME_COLUMN_SIZE);
215    }
216  }
217
218  public Boolean getEnabled() {
219    return enabled;
220  }
221
222  public void setEnabled(Boolean enabled) {
223    this.enabled = enabled;
224  }
225
226  public String getScope() {
227    return scope;
228  }
229
230  public void setScope(String scope) {
231    this.scope = scope;
232  }
233
234  public String getKey() {
235    return key;
236  }
237
238  public String getDeprecatedKey() {
239    return deprecatedKey;
240  }
241
242  public String getLanguageKey() {
243    return languageKey;
244  }
245
246  public void setLanguageKey(String lang) {
247    this.languageKey = lang;
248  }
249
250  public Integer getCopyResourceId() {
251    return copyResourceId;
252  }
253
254  public void setCopyResourceId(Integer i) {
255    this.copyResourceId = i;
256  }
257
258  /**
259   * @since 2.14
260   */
261  public Integer getPersonId() {
262    return personId;
263  }
264
265  /**
266   * @since 2.14
267   */
268  public ResourceModel setPersonId(Integer i) {
269    this.personId = i;
270    return this;
271  }
272
273  /**
274   * @throws IllegalArgumentException if the key is longer than KEY_SIZE
275   */
276  public void setKey(String key) {
277    checkSize(key);
278    this.key = key;
279  }
280
281  private void checkSize(String key) {
282    if (key.length() > KEY_SIZE) {
283      throw new IllegalArgumentException("Resource key is too long, max is " + KEY_SIZE + " characters. Got : " + key);
284    }
285  }
286
287  /**
288   * @throws IllegalArgumentException if the key is longer than KEY_SIZE
289   */
290  public void setDeprecatedKey(String deprecatedKey) {
291    checkSize(deprecatedKey);
292    this.deprecatedKey = deprecatedKey;
293  }
294
295  public Integer getRootId() {
296    return rootId;
297  }
298
299  public void setRootId(Integer rootId) {
300    this.rootId = rootId;
301  }
302
303  public String getPath() {
304    return path;
305  }
306
307  public ResourceModel setPath(@Nullable String path) {
308    if (path != null && path.length() > PATH_SIZE) {
309      throw new IllegalArgumentException("Resource path is too long, max is " + PATH_SIZE + " characters. Got : " + path);
310    }
311    this.path = path;
312    return this;
313  }
314
315  public String getQualifier() {
316    return qualifier;
317  }
318
319  public void setQualifier(String qualifier) {
320    this.qualifier = qualifier;
321  }
322
323  public Date getCreatedAt() {
324    return createdAt; // NOSONAR May expose internal representation by returning reference to mutable object
325  }
326
327  public void setCreatedAt(Date createdAt) {
328    this.createdAt = createdAt; // NOSONAR May expose internal representation by returning reference to mutable object
329  }
330
331  public String getUuid() {
332    return uuid;
333  }
334
335  public void setUuid(String uuid) {
336    this.uuid = uuid;
337  }
338
339  public String getProjectUuid() {
340    return projectUuid;
341  }
342
343  public void setProjectUuid(String projectUuid) {
344    this.projectUuid = projectUuid;
345  }
346
347  public String getModuleUuid() {
348    return moduleUuid;
349  }
350
351  public void setModuleUuid(String moduleUuid) {
352    this.moduleUuid = moduleUuid;
353  }
354
355  public String getModuleUuidPath() {
356    return moduleUuidPath;
357  }
358
359  public void setModuleUuidPath(String moduleUuidPath) {
360    this.moduleUuidPath = moduleUuidPath;
361  }
362
363  @Override
364  public boolean equals(Object obj) {
365    if (!(obj instanceof ResourceModel)) {
366      return false;
367    }
368    if (this == obj) {
369      return true;
370    }
371    ResourceModel other = (ResourceModel) obj;
372    return new EqualsBuilder()
373      .append(key, other.key)
374      .append(enabled, other.enabled)
375      .append(rootId, other.rootId)
376      .isEquals();
377  }
378
379  @Override
380  public int hashCode() {
381    return new HashCodeBuilder(17, 37)
382      .append(key)
383      .append(enabled)
384      .append(rootId)
385      .toHashCode();
386  }
387
388  @Override
389  public String toString() {
390    return new ToStringBuilder(this)
391      .append("id", getId())
392      .append("key", key)
393      .append("deprecatedKey", deprecatedKey)
394      .append("scope", scope)
395      .append("qualifier", qualifier)
396      .append("name", name)
397      .append("longName", longName)
398      .append("lang", languageKey)
399      .append("enabled", enabled)
400      .append("rootId", rootId)
401      .append("path", path)
402      .append("copyResourceId", copyResourceId)
403      .append("personId", personId)
404      .append("createdAt", createdAt)
405      .toString();
406  }
407
408  @Override
409  public Object clone() {
410    ResourceModel clone = new ResourceModel(getScope(), getKey(), getQualifier(), getRootId(), getPath(), getName());
411    clone.setDescription(getDescription());
412    clone.setDeprecatedKey(getDeprecatedKey());
413    clone.setEnabled(getEnabled());
414    clone.setProjectLinks(getProjectLinks());
415    clone.setLanguageKey(getLanguageKey());
416    clone.setCopyResourceId(getCopyResourceId());
417    clone.setLongName(getLongName());
418    clone.setPersonId(getPersonId());
419    clone.setCreatedAt(getCreatedAt());
420    return clone;
421  }
422
423  /**
424   * Maps a resource to a resource model and returns the resource
425   */
426  public static ResourceModel build(Resource resource) {
427    ResourceModel model = new ResourceModel();
428    model.setEnabled(Boolean.TRUE);
429    model.setDescription(resource.getDescription());
430    model.setKey(resource.getEffectiveKey());
431    model.setPath(resource.getPath());
432    Language lang = resource.getLanguage();
433    if (lang != null) {
434      model.setLanguageKey(lang.getKey());
435    }
436    if (StringUtils.isNotBlank(resource.getName())) {
437      model.setName(resource.getName());
438    } else {
439      model.setName(resource.getKey());
440    }
441    model.setLongName(resource.getLongName());
442    model.setQualifier(resource.getQualifier());
443    model.setScope(resource.getScope());
444    return model;
445  }
446
447}