Coverage Report - de.smartics.properties.spi.config.definition.DefinitionKeyHelper
 
Classes in this File Line Coverage Branch Coverage Complexity
DefinitionKeyHelper
0%
0/56
0%
0/26
3
 
 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.spi.config.definition;
 17  
 
 18  
 import static de.smartics.properties.api.config.domain.key.ApplicationId.ANY_APP;
 19  
 import static de.smartics.properties.api.config.domain.key.EnvironmentId.ANY_ENV;
 20  
 
 21  
 import java.io.Serializable;
 22  
 import java.util.StringTokenizer;
 23  
 
 24  
 import javax.annotation.concurrent.ThreadSafe;
 25  
 
 26  
 import de.smartics.properties.api.config.domain.key.ApplicationId;
 27  
 import de.smartics.properties.api.config.domain.key.ConfigurationKey;
 28  
 import de.smartics.properties.api.config.domain.key.EnvironmentId;
 29  
 import de.smartics.util.lang.Arguments;
 30  
 
 31  
 /**
 32  
  * Derives a {@link ConfigurationKey} from a path found in a definition file.
 33  
  */
 34  
 @ThreadSafe
 35  
 public final class DefinitionKeyHelper implements Serializable
 36  
 {
 37  
   // ********************************* Fields *********************************
 38  
 
 39  
   // --- constants ------------------------------------------------------------
 40  
 
 41  
   /**
 42  
    * The class version identifier.
 43  
    */
 44  
   private static final long serialVersionUID = 1L;
 45  
 
 46  
   // --- members --------------------------------------------------------------
 47  
 
 48  
   /**
 49  
    * The context to evaluate the configuration keys from properties file paths.
 50  
    *
 51  
    * @serial
 52  
    */
 53  
   private final PropertiesDefinitionContext context;
 54  
 
 55  
   // ****************************** Initializer *******************************
 56  
 
 57  
   // ****************************** Constructors ******************************
 58  
 
 59  
   /**
 60  
    * Convenience constructor using the default TLDs and not registering any
 61  
    * environments, nodes or groups.
 62  
    */
 63  
   public DefinitionKeyHelper()
 64  
   {
 65  0
     this(new PropertiesDefinitionContext());
 66  0
   }
 67  
 
 68  
   /**
 69  
    * Default constructor.
 70  
    *
 71  
    * @param context the context to evaluate the configuration keys from
 72  
    *          properties file paths.
 73  
    * @throws NullPointerException if {@code context} is <code>null</code>.
 74  
    */
 75  
   public DefinitionKeyHelper(final PropertiesDefinitionContext context)
 76  
     throws NullPointerException
 77  0
   {
 78  0
     Arguments.checkNotNull("context", context);
 79  
 
 80  0
     this.context = context;
 81  0
   }
 82  
 
 83  
   // ****************************** Inner Classes *****************************
 84  
 
 85  
   // ********************************* Methods ********************************
 86  
 
 87  
   // --- init -----------------------------------------------------------------
 88  
 
 89  
   // --- get&set --------------------------------------------------------------
 90  
 
 91  
   // --- business -------------------------------------------------------------
 92  
 
 93  
   /**
 94  
    * Parses the given path to create a configuration key.
 95  
    * <p>
 96  
    * The expected syntax is as follows:
 97  
    * </p>
 98  
    * <ol>
 99  
    * <li><code>/environment</code></li>
 100  
    * <li><code>/environment/node</code></li>
 101  
    * <li><code>/environment/node/group</code></li>
 102  
    * <li><code>/environment/node/group/application</code></li>
 103  
    * <li><code>/environment/node/group/application/version</code></li>
 104  
    * <li><code>/environment/group</code></li>
 105  
    * <li><code>/environment/group/application</code></li>
 106  
    * <li><code>/environment/group/application/version</code></li>
 107  
    * <li><code>/group</code></li>
 108  
    * <li><code>/group/application</code></li>
 109  
    * <li><code>/group/application/version</code></li>
 110  
    * </ol>
 111  
    * <p>
 112  
    * A file ending with properties following the path will be chopped.
 113  
    * </p>
 114  
    * <p>
 115  
    * The parser has to determine whether a part of the path is a
 116  
    * <code>environment</code>, a <code>node</code> or a <code>group</code>.
 117  
    * Since a <code>node</code> is always prefixed by an <code>environment</code>
 118  
    * only the following two cases have to be dealt with:
 119  
    * </p>
 120  
    * <ol>
 121  
    * <li><code>environment</code> vs. <code>group</code></li>
 122  
    * <li><code>node</code> vs. <code>group</code></li>
 123  
    * </ol>
 124  
    * <p>
 125  
    * <code>group</code>s start with
 126  
    * </p>
 127  
    * <ol>
 128  
    * <li>A TLD as registered by default by
 129  
    * {@link PropertiesDefinitionContext#DEFAULT_TLDS} or explicitly
 130  
    * registered with {@link PropertiesDefinitionContext}</li>
 131  
    * <li>Two letters followed by a dot (<code>.</code>)</li>
 132  
    * <li>Any sequence of characters that is explicitly registered as a group in
 133  
    * the <code>definition.xml</code> file</li>
 134  
    * </ol>
 135  
    * <p>
 136  
    * <code>environment</code>s and <code>node</code>s do not start as
 137  
    * <code>groups</code> except they are explicitly registered in the
 138  
    * <code>definition.xml</code> file.
 139  
    * </p>
 140  
    *
 141  
    * @param pathWithFile the path to parse.
 142  
    * @return the configuration key.
 143  
    * @throws IllegalArgumentException if the given {@code path} is not valid
 144  
    *           according to the rules given above.
 145  
    */
 146  
   public ConfigurationKey parse(final String pathWithFile)
 147  
     throws IllegalArgumentException
 148  
   {
 149  0
     final ConfigurationKey explicitKey = fetchExplicitKey(pathWithFile);
 150  0
     if (explicitKey != null)
 151  
     {
 152  0
       return explicitKey;
 153  
     }
 154  
 
 155  0
     final String path = chopFile(pathWithFile);
 156  
 
 157  0
     final StringTokenizer tokenizer = new StringTokenizer(path, "/");
 158  0
     if (tokenizer.hasMoreTokens())
 159  
     {
 160  
       final ConfigurationKey key;
 161  
 
 162  0
       final String token = tokenizer.nextToken();
 163  0
       if (isGroup(token))
 164  
       {
 165  0
         key = parseApplicationKey(token, tokenizer);
 166  
       }
 167  
       else
 168  
       {
 169  0
         key = parseEnvironmentKey(token, tokenizer);
 170  
       }
 171  
 
 172  0
       return key;
 173  
     }
 174  
 
 175  0
     return new ConfigurationKey(EnvironmentId.ANY_ENV, ApplicationId.ANY_APP);
 176  
     // throw new IllegalArgumentException(
 177  
     // "Path '" + path + "' does not contain an environment or group.");
 178  
   }
 179  
 
 180  
   private static String chopFile(final String pathWithFile)
 181  
   {
 182  0
     if (pathWithFile.endsWith(".properties"))
 183  
     {
 184  0
       final int lastSlash = pathWithFile.lastIndexOf('/');
 185  0
       if (lastSlash != -1)
 186  
       {
 187  0
         final String path = pathWithFile.substring(0, lastSlash);
 188  0
         return path;
 189  
       }
 190  
 
 191  0
       return "";
 192  
     }
 193  0
     return pathWithFile;
 194  
   }
 195  
 
 196  
   private ConfigurationKey fetchExplicitKey(final String path)
 197  
   {
 198  0
     ConfigurationKey key = context.getKey(path);
 199  0
     if (key == null)
 200  
     {
 201  0
       key = context.getKey(null);
 202  
     }
 203  0
     return key;
 204  
   }
 205  
 
 206  
   private boolean isGroup(final String token)
 207  
   {
 208  0
     if (context.isRegisteredEnvironment(token)
 209  
         || context.isRegisteredNode(token))
 210  
     {
 211  0
       return false;
 212  
     }
 213  
 
 214  0
     return context.isGroup(token);
 215  
   }
 216  
 
 217  
   private ConfigurationKey parseEnvironmentKey(final String environment,
 218  
       final StringTokenizer tokenizer)
 219  
   {
 220  
     final EnvironmentId envId;
 221  
     final ApplicationId appId;
 222  0
     if (tokenizer.hasMoreTokens())
 223  
     {
 224  0
       final String token = tokenizer.nextToken();
 225  0
       if (isGroup(token))
 226  
       {
 227  0
         envId = new EnvironmentId(environment);
 228  0
         appId = parseAppId(token, tokenizer);
 229  
       }
 230  
       else
 231  
       {
 232  0
         envId = new EnvironmentId(environment, token);
 233  0
         if (tokenizer.hasMoreTokens())
 234  
         {
 235  0
           appId = parseAppId(tokenizer.nextToken(), tokenizer);
 236  
         }
 237  
         else
 238  
         {
 239  0
           appId = ANY_APP;
 240  
         }
 241  
       }
 242  0
     }
 243  
     else
 244  
     {
 245  0
       envId = new EnvironmentId(environment);
 246  0
       appId = ANY_APP;
 247  
     }
 248  
 
 249  0
     final ConfigurationKey key = new ConfigurationKey(envId, appId);
 250  
 
 251  0
     return key;
 252  
   }
 253  
 
 254  
   private ApplicationId parseAppId(final String group,
 255  
       final StringTokenizer tokenizer)
 256  
   {
 257  0
     String artifact = null;
 258  0
     String version = null;
 259  0
     if (tokenizer.hasMoreTokens())
 260  
     {
 261  0
       artifact = tokenizer.nextToken();
 262  0
       if (tokenizer.hasMoreTokens())
 263  
       {
 264  0
         version = tokenizer.nextToken();
 265  
       }
 266  
     }
 267  
 
 268  0
     return new ApplicationId(group, artifact, version);
 269  
   }
 270  
 
 271  
   private ConfigurationKey parseApplicationKey(final String group,
 272  
       final StringTokenizer tokenizer)
 273  
   {
 274  0
     final EnvironmentId envId = ANY_ENV;
 275  0
     final ApplicationId appId = parseAppId(group, tokenizer);
 276  
 
 277  0
     return new ConfigurationKey(envId, appId);
 278  
   }
 279  
 
 280  
   // --- object basics --------------------------------------------------------
 281  
 
 282  
 }