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 }