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     */
020    package org.sonar.api.database.model;
021    
022    import org.apache.commons.lang.StringUtils;
023    import org.apache.commons.lang.builder.EqualsBuilder;
024    import org.apache.commons.lang.builder.HashCodeBuilder;
025    import org.apache.commons.lang.builder.ToStringBuilder;
026    import org.hibernate.annotations.BatchSize;
027    import org.sonar.api.database.BaseIdentifiable;
028    import org.sonar.api.resources.ProjectLink;
029    import org.sonar.api.resources.Resource;
030    
031    import javax.annotation.Nullable;
032    import javax.persistence.CascadeType;
033    import javax.persistence.Column;
034    import javax.persistence.Entity;
035    import javax.persistence.FetchType;
036    import javax.persistence.OneToMany;
037    import javax.persistence.Table;
038    import javax.persistence.Temporal;
039    import javax.persistence.TemporalType;
040    
041    import java.util.ArrayList;
042    import java.util.Date;
043    import java.util.List;
044    
045    /**
046     * Class to map resource with hibernate model
047     */
048    @Entity
049    @Table(name = "projects")
050    public 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        checkSize(key);
265        this.key = key;
266      }
267    
268      private void checkSize(String key) {
269        if (key.length() > KEY_SIZE) {
270          throw new IllegalArgumentException("Resource key is too long, max is " + KEY_SIZE + " characters. Got : " + key);
271        }
272      }
273    
274      /**
275       * @throws IllegalArgumentException if the key is longer than KEY_SIZE
276       */
277      public void setDeprecatedKey(String deprecatedKey) {
278        checkSize(deprecatedKey);
279        this.deprecatedKey = deprecatedKey;
280      }
281    
282      public Integer getRootId() {
283        return rootId;
284      }
285    
286      public void setRootId(Integer rootId) {
287        this.rootId = rootId;
288      }
289    
290      public String getPath() {
291        return path;
292      }
293    
294      public ResourceModel setPath(@Nullable String path) {
295        if (path != null && path.length() > PATH_SIZE) {
296          throw new IllegalArgumentException("Resource path is too long, max is " + PATH_SIZE + " characters. Got : " + path);
297        }
298        this.path = path;
299        return this;
300      }
301    
302      public String getQualifier() {
303        return qualifier;
304      }
305    
306      public void setQualifier(String qualifier) {
307        this.qualifier = qualifier;
308      }
309    
310      public Date getCreatedAt() {
311        return createdAt; // NOSONAR May expose internal representation by returning reference to mutable object
312      }
313    
314      public void setCreatedAt(Date createdAt) {
315        this.createdAt = createdAt; // NOSONAR May expose internal representation by returning reference to mutable object
316      }
317    
318      @Override
319      public boolean equals(Object obj) {
320        if (!(obj instanceof ResourceModel)) {
321          return false;
322        }
323        if (this == obj) {
324          return true;
325        }
326        ResourceModel other = (ResourceModel) obj;
327        return new EqualsBuilder()
328          .append(key, other.key)
329          .append(enabled, other.enabled)
330          .append(rootId, other.rootId)
331          .isEquals();
332      }
333    
334      @Override
335      public int hashCode() {
336        return new HashCodeBuilder(17, 37)
337          .append(key)
338          .append(enabled)
339          .append(rootId)
340          .toHashCode();
341      }
342    
343      @Override
344      public String toString() {
345        return new ToStringBuilder(this)
346          .append("id", getId())
347          .append("key", key)
348          .append("deprecatedKey", deprecatedKey)
349          .append("scope", scope)
350          .append("qualifier", qualifier)
351          .append("name", name)
352          .append("longName", longName)
353          .append("lang", languageKey)
354          .append("enabled", enabled)
355          .append("rootId", rootId)
356          .append("path", path)
357          .append("copyResourceId", copyResourceId)
358          .append("personId", personId)
359          .append("createdAt", createdAt)
360          .toString();
361      }
362    
363      @Override
364      public Object clone() {
365        ResourceModel clone = new ResourceModel(getScope(), getKey(), getQualifier(), getRootId(), getPath(), getName());
366        clone.setDescription(getDescription());
367        clone.setDeprecatedKey(getDeprecatedKey());
368        clone.setEnabled(getEnabled());
369        clone.setProjectLinks(getProjectLinks());
370        clone.setLanguageKey(getLanguageKey());
371        clone.setCopyResourceId(getCopyResourceId());
372        clone.setLongName(getLongName());
373        clone.setPersonId(getPersonId());
374        clone.setCreatedAt(getCreatedAt());
375        return clone;
376      }
377    
378      /**
379       * Maps a resource to a resource model and returns the resource
380       */
381      public static ResourceModel build(Resource resource) {
382        ResourceModel model = new ResourceModel();
383        model.setEnabled(Boolean.TRUE);
384        model.setDescription(resource.getDescription());
385        model.setKey(resource.getEffectiveKey());
386        model.setPath(resource.getPath());
387        if (resource.getLanguage() != null) {
388          model.setLanguageKey(resource.getLanguage().getKey());
389        }
390        if (StringUtils.isNotBlank(resource.getName())) {
391          model.setName(resource.getName());
392        } else {
393          model.setName(resource.getKey());
394        }
395        model.setLongName(resource.getLongName());
396        model.setQualifier(resource.getQualifier());
397        model.setScope(resource.getScope());
398        return model;
399      }
400    
401    }