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.Language;
029    import org.sonar.api.resources.ProjectLink;
030    import org.sonar.api.resources.Resource;
031    
032    import javax.annotation.Nullable;
033    import javax.persistence.CascadeType;
034    import javax.persistence.Column;
035    import javax.persistence.Entity;
036    import javax.persistence.FetchType;
037    import javax.persistence.OneToMany;
038    import javax.persistence.Table;
039    import javax.persistence.Temporal;
040    import javax.persistence.TemporalType;
041    
042    import java.util.ArrayList;
043    import java.util.Date;
044    import java.util.List;
045    
046    /**
047     * Class to map resource with hibernate model
048     */
049    @Entity
050    @Table(name = "projects")
051    public 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    }