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