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 }