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 }