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