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 }