1 /* 2 * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package de.smartics.properties.api.core.domain; 17 18 import java.io.Serializable; 19 import java.net.URL; 20 import java.util.ArrayList; 21 import java.util.List; 22 import java.util.Locale; 23 24 import javax.annotation.CheckForNull; 25 import javax.annotation.concurrent.ThreadSafe; 26 27 import org.apache.commons.lang.LocaleUtils; 28 import org.apache.commons.lang.StringUtils; 29 import org.apache.commons.lang.builder.ToStringBuilder; 30 31 import de.smartics.properties.api.core.context.alias.AliasTraverser; 32 import de.smartics.properties.api.core.context.alias.DuplicateAliasException; 33 import de.smartics.properties.api.core.context.alias.PropertyAliasMapping; 34 import de.smartics.properties.api.core.context.alias.UnknownAliasException; 35 import de.smartics.util.lang.Arguments; 36 import de.smartics.util.lang.BlankArgumentException; 37 38 /** 39 * Defines the configuration for smartics properties configuration. 40 * <p> 41 * A context provides information for all its properties. Not all properties of 42 * an application may refer to the same context. 43 * </p> 44 * 45 * @impl For testing purposes please refer to 46 * <code>de.smartics.properties.test.core.PropertiesContextBuilder</code>. 47 */ 48 @ThreadSafe 49 public final class PropertiesContext implements Serializable 50 { // NOPMD 51 // ********************************* Fields ********************************* 52 53 // --- constants ------------------------------------------------------------ 54 55 /** 56 * The class version identifier. 57 */ 58 private static final long serialVersionUID = 1L; 59 60 /** 61 * The path to the folder within <code>META-INF</code> where properties 62 * resources are located. 63 * <p> 64 * The value of this constant is {@value}. 65 * </p> 66 */ 67 public static final String META_INF_HOME = "META-INF/smartics-properties"; 68 69 /** 70 * The name of the properties configuration file that provides information for 71 * declarations. This file provides information in archives that provide 72 * property meta data (descriptors). 73 * <p> 74 * The value of this constant is {@value}. 75 * </p> 76 */ 77 public static final String DECLARATION_FILE_NAME = "declaration.xml"; 78 79 /** 80 * The name of the properties configuration file that provides information for 81 * definitions. This file provides information in archives that provide 82 * property values. 83 * <p> 84 * The value of this constant is {@value}. 85 * </p> 86 */ 87 public static final String DEFINITION_FILE_NAME = "definition.xml"; 88 89 /** 90 * The default location of the properties declaration configuration file. 91 * <p> 92 * The value of this constant is {@value}. 93 * </p> 94 */ 95 public static final String DECLARATION_FILE = META_INF_HOME + '/' 96 + DECLARATION_FILE_NAME; 97 98 /** 99 * The default location of the properties definition configuration file. 100 * <p> 101 * The value of this constant is {@value}. 102 * </p> 103 */ 104 public static final String DEFINITION_FILE = META_INF_HOME + '/' 105 + DEFINITION_FILE_NAME; 106 107 /** 108 * The location of the property reports within the <code>META-INF</code> 109 * folder. 110 * <p> 111 * The value of this constant is {@value}. 112 * </p> 113 */ 114 private static final String META_INF_PROPERTY_REPORT = META_INF_HOME 115 + "/property-report/"; 116 117 /** 118 * The location of the property set reports within the <code>META-INF</code> 119 * folder. 120 * <p> 121 * The value of this constant is {@value}. 122 * </p> 123 */ 124 public static final String META_INF_PROPERTY_SET_REPORT = 125 META_INF_HOME + "/property-set-report/"; 126 127 /** 128 * Specifies the default report location on the home page. 129 * <p> 130 * The value of this constant is {@value}. 131 * </p> 132 */ 133 private static final String DEFAULT_REPORT_LOCATION = "/property"; 134 135 // --- members -------------------------------------------------------------- 136 137 /** 138 * The list of supported locales. The list contains locales the context 139 * provides localized information for. 140 * 141 * @serial 142 */ 143 private final List<Locale> locales; 144 145 /** 146 * The URL to the home page of the project. May be <code>null</code>. 147 * 148 * @serial 149 */ 150 private final String homePageUrl; 151 152 /** 153 * The URL to the root directory of smartics properties reports. May be 154 * <code>null</code>. 155 * 156 * @serial 157 */ 158 private final String propertiesReportUrl; 159 160 /** 161 * The mapping of alias names of property reports to their physical names. 162 * 163 * @impl Although {@link PropertyAliasMapping} is not thread-safe, this 164 * instance provides no write-access to it. 165 * @serial 166 */ 167 private final PropertyAliasMapping aliasMapping; 168 169 // ****************************** Initializer ******************************* 170 171 // ****************************** Constructors ****************************** 172 173 /** 174 * Default constructor. 175 */ 176 private PropertiesContext(final Builder builder) 177 { 178 this.locales = builder.locales; 179 this.homePageUrl = builder.homePageUrl; 180 this.propertiesReportUrl = builder.propertiesReportUrl; 181 this.aliasMapping = builder.aliasMapping; 182 } 183 184 // ****************************** Inner Classes ***************************** 185 186 /** 187 * The builder of {@link PropertiesContext}. 188 */ 189 public static final class Builder 190 { 191 // ******************************** Fields ******************************** 192 193 // --- constants ---------------------------------------------------------- 194 195 // --- members ------------------------------------------------------------ 196 197 /** 198 * The list of supported locales. The list contains locales the context 199 * provides localized information for. 200 */ 201 private List<Locale> locales = new ArrayList<Locale>(); 202 203 /** 204 * The URL to the home page of the project. 205 */ 206 private String homePageUrl; 207 208 /** 209 * The URL to the root directory of smartics properties reports. 210 */ 211 private String propertiesReportUrl; 212 213 /** 214 * The mapping of alias names of property reports to their physical names. 215 */ 216 private final PropertyAliasMapping aliasMapping = 217 new PropertyAliasMapping(); 218 219 // ***************************** Initializer ****************************** 220 221 // ***************************** Constructors ***************************** 222 223 // ***************************** Inner Classes **************************** 224 225 // ******************************** Methods ******************************* 226 227 // --- init --------------------------------------------------------------- 228 229 // --- get&set ------------------------------------------------------------ 230 231 /** 232 * Sets the list of supported locales. The list contains locales the context 233 * provides localized information for. 234 * 235 * @param locales the list of supported locales. 236 * @return a reference to the builder. 237 * @throws NullPointerException if {@code locales} is <code>null</code>. 238 */ 239 public Builder withLocales(final List<Locale> locales) 240 throws NullPointerException 241 { 242 Arguments.checkNotNull("locales", locales); 243 this.locales = locales; 244 return this; 245 } 246 247 /** 248 * Sets the URL to the home page of the project. 249 * 250 * @param homePageUrl the URL to the home page of the project. 251 * @return a reference to the builder. 252 * @throws IllegalArgumentException if {@code homePageUrl} is blank. 253 */ 254 public Builder withHomePageUrl(final String homePageUrl) 255 throws IllegalArgumentException 256 { 257 Arguments.checkNotBlank("homePageUrl", homePageUrl); 258 this.homePageUrl = normalizeUrl(homePageUrl); 259 return this; 260 } 261 262 /** 263 * Sets the URL to the root directory of smartics properties reports. 264 * 265 * @param propertiesReportUrl the URL to the root directory of smartics 266 * properties reports. 267 * @return a reference to the builder. 268 * @throws IllegalArgumentException if {@code propertiesReportUrl} is blank. 269 */ 270 public Builder withPropertiesReportUrl(final String propertiesReportUrl) 271 throws IllegalArgumentException 272 { 273 Arguments.checkNotBlank("propertiesReportUrl", propertiesReportUrl); 274 this.propertiesReportUrl = normalizeUrl(propertiesReportUrl); 275 return this; 276 } 277 278 /** 279 * Adds a new alias to the given physical resource. 280 * 281 * @param alias the new alias. 282 * @param physical the resource the alias refers to. 283 * @return a reference to the builder. 284 * @throws BlankArgumentException if either {@code alias} or 285 * {@code physical} is blank. 286 * @throws DuplicateAliasException if there is already an alias registered 287 * that points to a different physical resource. 288 */ 289 public Builder withAlias(final String alias, final String physical) 290 throws BlankArgumentException, DuplicateAliasException 291 { 292 aliasMapping.add(alias, physical); 293 return this; 294 } 295 296 // --- business ----------------------------------------------------------- 297 298 /** 299 * Creates the instance. 300 * 301 * @return the new instance. 302 */ 303 public PropertiesContext build() 304 { 305 return new PropertiesContext(this); 306 } 307 308 // --- object basics ------------------------------------------------------ 309 } 310 311 // ********************************* Methods ******************************** 312 313 // --- init ----------------------------------------------------------------- 314 315 // --- factory -------------------------------------------------------------- 316 317 /** 318 * Creates an empty context. 319 * 320 * @return an empty context. 321 */ 322 public static PropertiesContext createEmptyContext() 323 { 324 return new Builder().build(); 325 } 326 327 // --- get&set -------------------------------------------------------------- 328 329 /** 330 * Returns the URL to the home page of the project. 331 * 332 * @return the URL to the home page of the project. May be <code>null</code>. 333 */ 334 @CheckForNull 335 public String getHomePageUrl() 336 { 337 return homePageUrl; 338 } 339 340 /** 341 * Returns the URL to the root directory of smartics properties reports. 342 * 343 * @return the URL to the root directory of smartics properties reports. May 344 * be <code>null</code>. 345 */ 346 @CheckForNull 347 public String getPropertiesReportUrl() 348 { 349 if (propertiesReportUrl == null && homePageUrl != null) 350 { 351 return homePageUrl + DEFAULT_REPORT_LOCATION; 352 } 353 return propertiesReportUrl; 354 } 355 356 /** 357 * Returns the URL to the index document of smartics properties reports. 358 * 359 * @return the URL to the index document of smartics properties reports. May 360 * be <code>null</code>. 361 */ 362 @CheckForNull 363 public String getPropertiesReportIndexUrl() 364 { 365 return createReportUrl("smartics-properties-report"); 366 } 367 368 /** 369 * Returns the list of supported locales. The list contains locales the 370 * context provides localized information for. 371 * 372 * @return the list of supported locales. 373 */ 374 public List<Locale> getLocales() 375 { 376 return locales; 377 } 378 379 // --- business ------------------------------------------------------------- 380 381 private static String normalizeUrl(final String url) 382 { 383 return StringUtils.chomp(url, "/"); 384 } 385 386 /** 387 * Returns the URL to the relative target. 388 * 389 * @param target the relative URL. 390 * @return the absolute URL to the report or <code>null</code> if no root URL 391 * is provided by the context. 392 * @throws IllegalArgumentException of {@code target} is blank. 393 */ 394 public String createReportUrl(final String target) 395 throws IllegalArgumentException 396 { 397 Arguments.checkNotBlank("target", target); 398 399 if (StringUtils.isEmpty(propertiesReportUrl)) 400 { 401 return null; 402 } 403 404 final String htmlFile = target + ".html"; 405 if (target.charAt(0) == '/') 406 { 407 return propertiesReportUrl + htmlFile; 408 } 409 else 410 { 411 return propertiesReportUrl + '/' + htmlFile; 412 } 413 } 414 415 /** 416 * Returns the URL to the report documentation for the given descriptor. 417 * 418 * @param descriptor the properties descriptor whose report documentation URL 419 * is requested. 420 * @return the absolute URL to the report. 421 */ 422 public String createReportUrl(final PropertyDescriptor descriptor) 423 { 424 final String target = resolve(descriptor); 425 final String url = createReportUrl(target); 426 return url; 427 } 428 429 private static String resolve(final PropertyDescriptor descriptor) 430 { 431 final String target = descriptor.getKey().toString(); 432 return target; 433 } 434 435 /** 436 * Returns the URL to the XML report in the META-INF directory of the given 437 * descriptor. 438 * 439 * @param descriptor the properties descriptor whose XML report URL is 440 * requested. 441 * @return the class loader root rooted path. 442 */ 443 public String createMetaInfPath(final PropertyDescriptor descriptor) 444 { 445 return createMetaInfPath(descriptor, null); 446 } 447 448 /** 449 * Returns the URL to the XML report in the META-INF directory of the given 450 * descriptor. 451 * 452 * @param descriptor the properties descriptor whose XML report URL is 453 * requested. 454 * @param locale the locale to determine the comments. 455 * @return the class loader root rooted path. 456 */ 457 @SuppressWarnings("unchecked") 458 public String createMetaInfPath(final PropertyDescriptor descriptor, 459 final Locale locale) 460 { 461 final String target = resolve(descriptor); 462 463 if (locale != null) 464 { 465 final List<Locale> locales = LocaleUtils.localeLookupList(locale); 466 for (final Locale currentLocale : locales) 467 { 468 final String path = 469 META_INF_PROPERTY_REPORT + target + '_' + currentLocale + ".xml"; 470 final ClassLoader loader = descriptor.getClass().getClassLoader(); // NOPMD 471 final URL resource = loader.getResource(path); 472 if (resource != null) 473 { 474 return path; 475 } 476 } 477 } 478 479 final String path = META_INF_PROPERTY_REPORT + target + ".xml"; 480 return path; 481 } 482 483 /** 484 * Resolves the alias to the target it points to. 485 * 486 * @param alias the alias whose physical resource is requested. 487 * @return the target the alias points to. 488 * @throws BlankArgumentException if {@code alias} is blank. 489 * @throws UnknownAliasException if the alias is not known. 490 */ 491 public String resolve(final String alias) throws BlankArgumentException, 492 UnknownAliasException 493 { 494 return aliasMapping.get(alias); 495 } 496 497 /** 498 * Traverses the registered aliases. 499 * 500 * @param traverser the traverser to use. 501 * @throws NullPointerException if {@code traverser} is <code>null</code>. 502 */ 503 public void traverseAliases(final AliasTraverser traverser) 504 throws NullPointerException 505 { 506 aliasMapping.traverse(traverser); 507 } 508 509 /** 510 * Checks whether any aliases are registered. 511 * 512 * @return <code>true</code> if at least one alias is registered, 513 * <code>false</code> otherwise. 514 */ 515 public boolean hasAliases() 516 { 517 return !aliasMapping.isEmpty(); 518 } 519 520 // --- object basics -------------------------------------------------------- 521 522 /** 523 * Returns the string representation of the object. 524 * 525 * @return the string representation of the object. 526 */ 527 @Override 528 public String toString() 529 { 530 return ToStringBuilder.reflectionToString(this); 531 } 532 }