001/* 002 * SonarQube, open source software quality management tool. 003 * Copyright (C) 2008-2013 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 */ 020package org.sonar.api.database.model; 021 022import org.apache.commons.lang.StringUtils; 023import org.apache.commons.lang.builder.EqualsBuilder; 024import org.apache.commons.lang.builder.HashCodeBuilder; 025import org.apache.commons.lang.builder.ToStringBuilder; 026import org.hibernate.annotations.BatchSize; 027import org.sonar.api.database.BaseIdentifiable; 028import org.sonar.api.resources.ProjectLink; 029import org.sonar.api.resources.Resource; 030 031import javax.annotation.Nullable; 032import javax.persistence.CascadeType; 033import javax.persistence.Column; 034import javax.persistence.Entity; 035import javax.persistence.FetchType; 036import javax.persistence.OneToMany; 037import javax.persistence.Table; 038import javax.persistence.Temporal; 039import javax.persistence.TemporalType; 040 041import java.util.ArrayList; 042import java.util.Date; 043import java.util.List; 044 045/** 046 * Class to map resource with hibernate model 047 */ 048@Entity 049@Table(name = "projects") 050public 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 if (key.length() > KEY_SIZE) { 265 throw new IllegalArgumentException("Resource key is too long, max is " + KEY_SIZE + " characters. Got : " + key); 266 } 267 this.key = key; 268 } 269 270 /** 271 * @throws IllegalArgumentException if the key is longer than KEY_SIZE 272 */ 273 public void setDeprecatedKey(String deprecatedKey) { 274 if (deprecatedKey.length() > KEY_SIZE) { 275 throw new IllegalArgumentException("Resource deprecated key is too long, max is " + KEY_SIZE + " characters. Got : " + deprecatedKey); 276 } 277 this.deprecatedKey = deprecatedKey; 278 } 279 280 public Integer getRootId() { 281 return rootId; 282 } 283 284 public void setRootId(Integer rootId) { 285 this.rootId = rootId; 286 } 287 288 public String getPath() { 289 return path; 290 } 291 292 public ResourceModel setPath(@Nullable String path) { 293 if (path != null && path.length() > PATH_SIZE) { 294 throw new IllegalArgumentException("Resource path is too long, max is " + PATH_SIZE + " characters. Got : " + path); 295 } 296 this.path = path; 297 return this; 298 } 299 300 public String getQualifier() { 301 return qualifier; 302 } 303 304 public void setQualifier(String qualifier) { 305 this.qualifier = qualifier; 306 } 307 308 public Date getCreatedAt() { 309 return createdAt; // NOSONAR May expose internal representation by returning reference to mutable object 310 } 311 312 public void setCreatedAt(Date createdAt) { 313 this.createdAt = createdAt; // NOSONAR May expose internal representation by returning reference to mutable object 314 } 315 316 @Override 317 public boolean equals(Object obj) { 318 if (!(obj instanceof ResourceModel)) { 319 return false; 320 } 321 if (this == obj) { 322 return true; 323 } 324 ResourceModel other = (ResourceModel) obj; 325 return new EqualsBuilder() 326 .append(key, other.key) 327 .append(enabled, other.enabled) 328 .append(rootId, other.rootId) 329 .isEquals(); 330 } 331 332 @Override 333 public int hashCode() { 334 return new HashCodeBuilder(17, 37) 335 .append(key) 336 .append(enabled) 337 .append(rootId) 338 .toHashCode(); 339 } 340 341 @Override 342 public String toString() { 343 return new ToStringBuilder(this) 344 .append("id", getId()) 345 .append("key", key) 346 .append("deprecatedKey", deprecatedKey) 347 .append("scope", scope) 348 .append("qualifier", qualifier) 349 .append("name", name) 350 .append("longName", longName) 351 .append("lang", languageKey) 352 .append("enabled", enabled) 353 .append("rootId", rootId) 354 .append("path", path) 355 .append("copyResourceId", copyResourceId) 356 .append("personId", personId) 357 .append("createdAt", createdAt) 358 .toString(); 359 } 360 361 @Override 362 public Object clone() { 363 ResourceModel clone = new ResourceModel(getScope(), getKey(), getQualifier(), getRootId(), getPath(), getName()); 364 clone.setDescription(getDescription()); 365 clone.setDeprecatedKey(getDeprecatedKey()); 366 clone.setEnabled(getEnabled()); 367 clone.setProjectLinks(getProjectLinks()); 368 clone.setLanguageKey(getLanguageKey()); 369 clone.setCopyResourceId(getCopyResourceId()); 370 clone.setLongName(getLongName()); 371 clone.setPersonId(getPersonId()); 372 clone.setCreatedAt(getCreatedAt()); 373 return clone; 374 } 375 376 /** 377 * Maps a resource to a resource model and returns the resource 378 */ 379 public static ResourceModel build(Resource resource) { 380 ResourceModel model = new ResourceModel(); 381 model.setEnabled(Boolean.TRUE); 382 model.setDescription(resource.getDescription()); 383 model.setKey(resource.getEffectiveKey()); 384 model.setPath(resource.getPath()); 385 if (resource.getLanguage() != null) { 386 model.setLanguageKey(resource.getLanguage().getKey()); 387 } 388 if (StringUtils.isNotBlank(resource.getName())) { 389 model.setName(resource.getName()); 390 } else { 391 model.setName(resource.getKey()); 392 } 393 model.setLongName(resource.getLongName()); 394 model.setQualifier(resource.getQualifier()); 395 model.setScope(resource.getScope()); 396 return model; 397 } 398 399}