Coverage Report - de.smartics.properties.spi.core.metadata.projectdoc.ProjectdocMetaDataParser
 
Classes in this File Line Coverage Branch Coverage Complexity
ProjectdocMetaDataParser
0%
0/114
0%
0/28
1.957
ProjectdocMetaDataParser$1
N/A
N/A
N/A
N/A
1.957
ProjectdocMetaDataParser$ParserContext
0%
0/13
N/A
N/A
1.957
 
 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.core.metadata.projectdoc;
 17  
 
 18  
 import static de.smartics.util.lang.StaticAnalysis.UNCHECKED;
 19  
 
 20  
 import java.io.BufferedInputStream;
 21  
 import java.io.IOException;
 22  
 import java.io.InputStream;
 23  
 import java.util.List;
 24  
 import java.util.Locale;
 25  
 
 26  
 import javax.annotation.CheckForNull;
 27  
 
 28  
 import org.apache.commons.io.IOUtils;
 29  
 import org.jdom.Document;
 30  
 import org.jdom.Element;
 31  
 import org.jdom.JDOMException;
 32  
 import org.jdom.Namespace;
 33  
 import org.jdom.input.SAXBuilder;
 34  
 import org.jdom.output.XMLOutputter;
 35  
 import org.slf4j.Logger;
 36  
 import org.slf4j.LoggerFactory;
 37  
 
 38  
 import de.smartics.properties.api.core.domain.DocumentMetaData;
 39  
 import de.smartics.properties.api.core.domain.ProjectdocMetaData;
 40  
 import de.smartics.properties.api.core.domain.PropertiesContext;
 41  
 import de.smartics.properties.api.core.domain.PropertyDescriptor;
 42  
 import de.smartics.properties.spi.core.metadata.projectdoc.ProjectdocAnnotationCollector.Defaults;
 43  
 import de.smartics.util.lang.Arg;
 44  
 
 45  
 /**
 46  
  * Parses projectdoc information from property XML descriptors.
 47  
  */
 48  
 public abstract class ProjectdocMetaDataParser
 49  
 {
 50  
   // ********************************* Fields *********************************
 51  
 
 52  
   // --- constants ------------------------------------------------------------
 53  
 
 54  
   /**
 55  
    * Reference to the logger for this class.
 56  
    */
 57  0
   private static final Logger LOG = LoggerFactory
 58  
       .getLogger(ProjectdocMetaDataParser.class);
 59  
 
 60  
   // --- members --------------------------------------------------------------
 61  
 
 62  
   /**
 63  
    * The properties context to fetch information from the META-INF folder.
 64  
    */
 65  
   protected final PropertiesContext context;
 66  
 
 67  
   /**
 68  
    * The metadata defaults to use.
 69  
    */
 70  
   protected final Defaults defaults;
 71  
 
 72  
   // ****************************** Initializer *******************************
 73  
 
 74  
   // ****************************** Constructors ******************************
 75  
 
 76  
   /**
 77  
    * Convenience constructor without defaults.
 78  
    *
 79  
    * @param context the properties context to fetch information from the
 80  
    *          META-INF folder.
 81  
    * @throws NullPointerException if {@code context} is <code>null</code>.
 82  
    */
 83  
   protected ProjectdocMetaDataParser(final PropertiesContext context)
 84  
     throws NullPointerException
 85  
   {
 86  0
     this(context, null);
 87  0
   }
 88  
 
 89  
   /**
 90  
    * Default constructor.
 91  
    *
 92  
    * @param context the properties context to fetch information from the
 93  
    *          META-INF folder.
 94  
    * @param defaults the metadata defaults to use.
 95  
    * @throws NullPointerException if {@code context} is <code>null</code>.
 96  
    */
 97  
   protected ProjectdocMetaDataParser(final PropertiesContext context,
 98  
       final Defaults defaults) throws NullPointerException
 99  0
   {
 100  0
     this.context = Arg.checkNotNull("context", context);
 101  0
     this.defaults = defaults;
 102  0
   }
 103  
 
 104  
   // ****************************** Inner Classes *****************************
 105  
 
 106  
   /**
 107  
    * Provide access to information relevant for parsing and storing the parsed
 108  
    * information.
 109  
    */
 110  0
   public final class ParserContext
 111  
   {
 112  
 
 113  
     // ******************************** Fields ********************************
 114  
 
 115  
     // --- constants ----------------------------------------------------------
 116  
 
 117  
     // --- members ------------------------------------------------------------
 118  
 
 119  
     /**
 120  
      * The descriptor whose comments are requested to be parsed.
 121  
      */
 122  
     private final PropertyDescriptor descriptor;
 123  
 
 124  
     /**
 125  
      * The locale to select the comments.
 126  
      */
 127  
     private final Locale locale;
 128  
 
 129  
     /**
 130  
      * The identifier of the document.
 131  
      */
 132  
     private final String systemId;
 133  
 
 134  
     /**
 135  
      * The parsed document to extract information from.
 136  
      */
 137  
     private final Document document;
 138  
 
 139  
     /**
 140  
      * The container where the parsed information is put to. You may safely cast
 141  
      * this instance to the type that has been passed to
 142  
      * {@link #parseBase(PropertyDescriptor, Locale, ProjectdocMetaData)}.
 143  
      */
 144  
     private final ProjectdocMetaData metaData;
 145  
 
 146  
     // ***************************** Initializer ******************************
 147  
 
 148  
     // ***************************** Constructors *****************************
 149  
 
 150  
     private ParserContext(final PropertyDescriptor descriptor,
 151  
         final Locale locale, final String systemId, final Document document,
 152  
         final ProjectdocMetaData metaData)
 153  0
     {
 154  0
       this.descriptor = descriptor;
 155  0
       this.locale = locale;
 156  0
       this.systemId = systemId;
 157  0
       this.document = document;
 158  0
       this.metaData = metaData;
 159  0
     }
 160  
 
 161  
     // ***************************** Inner Classes ****************************
 162  
 
 163  
     // ******************************** Methods *******************************
 164  
 
 165  
     // --- init ---------------------------------------------------------------
 166  
 
 167  
     // --- get&set ------------------------------------------------------------
 168  
 
 169  
     /**
 170  
      * Returns the descriptor whose comments are requested to be parsed.
 171  
      *
 172  
      * @return the descriptor whose comments are requested to be parsed.
 173  
      */
 174  
     public PropertyDescriptor getDescriptor()
 175  
     {
 176  0
       return descriptor;
 177  
     }
 178  
 
 179  
     /**
 180  
      * Returns the locale to select the comments.
 181  
      *
 182  
      * @return the locale to select the comments.
 183  
      */
 184  
     public Locale getLocale()
 185  
     {
 186  0
       return locale;
 187  
     }
 188  
 
 189  
     /**
 190  
      * Returns the identifier of the document.
 191  
      *
 192  
      * @return the identifier of the document.
 193  
      */
 194  
     public String getSystemId()
 195  
     {
 196  0
       return systemId;
 197  
     }
 198  
 
 199  
     /**
 200  
      * Returns the parsed document to extract information from.
 201  
      *
 202  
      * @return the parsed document to extract information from.
 203  
      */
 204  
     public Document getDocument()
 205  
     {
 206  0
       return document;
 207  
     }
 208  
 
 209  
     /**
 210  
      * Returns the container where the parsed information is put to. You may
 211  
      * safely cast this instance to the type that has been passed to
 212  
      * {@link #parseBase(PropertyDescriptor,Locale,ProjectdocMetaData)}.
 213  
      *
 214  
      * @return the container where the parsed information is put to.
 215  
      */
 216  
     public ProjectdocMetaData getMetaData()
 217  
     {
 218  0
       return metaData;
 219  
     }
 220  
 
 221  
     // --- business -----------------------------------------------------------
 222  
 
 223  
     // --- object basics ------------------------------------------------------
 224  
   }
 225  
 
 226  
   // ********************************* Methods ********************************
 227  
 
 228  
   // --- init -----------------------------------------------------------------
 229  
 
 230  
   // --- get&set --------------------------------------------------------------
 231  
 
 232  
   // --- business -------------------------------------------------------------
 233  
 
 234  
   /**
 235  
    * Parses the comments for the given descriptor.
 236  
    * <p>
 237  
    * This method is overridden by subclasses to return their specific
 238  
    * implementation of the {@link DocumentMetaData}.
 239  
    * </p>
 240  
    *
 241  
    * @param descriptor the descriptor whose comments are requested to be parsed.
 242  
    * @param locale the locale to select the comments.
 243  
    * @return the comments for the given locale or the default comments, if the
 244  
    *         locale is not supported. May be <code>null</code> if no information
 245  
    *         is provided.
 246  
    */
 247  
   @CheckForNull
 248  
   public ProjectdocMetaData parse(final PropertyDescriptor descriptor,
 249  
       final Locale locale)
 250  
   {
 251  
     try
 252  
     {
 253  0
       final ProjectdocMetaData metaData = new ProjectdocMetaData();
 254  0
       parseBase(descriptor, locale, metaData);
 255  0
       return metaData;
 256  
     }
 257  0
     catch (final MetaDataException e)
 258  
     {
 259  0
       LOG.warn("Cannot parse meta data for property descriptor '{}': {}",
 260  
           descriptor, e);
 261  0
       return null;
 262  
     }
 263  
   }
 264  
 
 265  
   /**
 266  
    * Parses the comments for the given descriptor.
 267  
    *
 268  
    * @param descriptor the descriptor whose comments are requested to be parsed.
 269  
    * @param locale the locale to select the comments.
 270  
    * @param metaData the container where the parsed information is put to.
 271  
    * @throws MetaDataException if the meta data cannot be read.
 272  
    */
 273  
   protected final void parseBase(final PropertyDescriptor descriptor,
 274  
       final Locale locale, final ProjectdocMetaData metaData)
 275  
     throws MetaDataException
 276  
   {
 277  0
     final String path = calcPath(descriptor, locale);
 278  
 
 279  0
     final ClassLoader loader = descriptor.getDeclaringType().getClassLoader();
 280  
 
 281  0
     InputStream input = null;
 282  
     try
 283  
     {
 284  0
       final InputStream resource = load(path, loader);
 285  0
       if (resource != null)
 286  
       {
 287  0
         input = new BufferedInputStream(resource);
 288  0
         parse(descriptor, path, locale, input, metaData);
 289  
       }
 290  
       else
 291  
       {
 292  0
         throw new MetaDataException(path);
 293  
       }
 294  
     }
 295  
     finally
 296  
     {
 297  0
       IOUtils.closeQuietly(input);
 298  0
     }
 299  0
   }
 300  
 
 301  
   /**
 302  
    * Calculates the path to a resource to be parsed.
 303  
    *
 304  
    * @param descriptor the descriptor to the resource.
 305  
    * @param locale the requested locale.
 306  
    * @return the path to the resource.
 307  
    */
 308  
   protected abstract String calcPath(final PropertyDescriptor descriptor,
 309  
       final Locale locale);
 310  
 
 311  
   private InputStream load(final String path, final ClassLoader loader)
 312  
   {
 313  0
     InputStream input = loader.getResourceAsStream(path);
 314  0
     if (input == null)
 315  
     {
 316  0
       input = loader.getResourceAsStream('/' + path);
 317  
     }
 318  0
     return input;
 319  
   }
 320  
 
 321  
   private DocumentMetaData parse(final PropertyDescriptor descriptor,
 322  
       final String systemId, final Locale locale, final InputStream input,
 323  
       final ProjectdocMetaData metaData) throws MetaDataException
 324  
   {
 325  
     try
 326  
     {
 327  0
       final SAXBuilder parser = new SAXBuilder();
 328  0
       final Document document = parser.build(input, systemId);
 329  
 
 330  0
       final Element rootNode = document.getRootElement();
 331  0
       addIdInfo(metaData, rootNode);
 332  0
       addFiling(metaData, rootNode);
 333  0
       addDescription(metaData, rootNode);
 334  
 
 335  0
       final ParserContext context =
 336  
           new ParserContext(descriptor, locale, systemId, document, metaData);
 337  0
       parseAdditional(context);
 338  
 
 339  0
       return metaData;
 340  
     }
 341  0
     catch (final JDOMException e)
 342  
     {
 343  0
       throw new MetaDataException(systemId, e);
 344  
     }
 345  0
     catch (final IOException e)
 346  
     {
 347  0
       throw new MetaDataException(systemId, e);
 348  
     }
 349  
   }
 350  
 
 351  
   /**
 352  
    * The hook that allows to add further information.
 353  
    *
 354  
    * @param context to provide access to information relevant for parsing and
 355  
    *          storing the parsed information.
 356  
    * @throws MetaDataException if the meta data cannot be read.
 357  
    */
 358  
   protected void parseAdditional(final ParserContext context)
 359  
     throws MetaDataException
 360  
   {
 361  0
   }
 362  
 
 363  
   private void addIdInfo(final ProjectdocMetaData metaData,
 364  
       final Element rootNode)
 365  
   {
 366  0
     if (defaults != null)
 367  
     {
 368  0
       metaData.setName(defaults.getName());
 369  0
       metaData.setSpace(defaults.getSpace());
 370  0
       metaData.setTitle(defaults.getTitle());
 371  
     }
 372  
 
 373  0
     final Element identification = rootNode.getChild("identification", getNs());
 374  0
     if (identification != null)
 375  
     {
 376  0
       final String space =
 377  
           identification.getChildTextNormalize("space", getNs());
 378  0
       metaData.setSpace(space);
 379  0
       final String title =
 380  
           identification.getChildTextNormalize("title", getNs());
 381  0
       metaData.setTitle(title);
 382  
     }
 383  
 
 384  0
     final String name = rootNode.getChildTextNormalize("name", getNs());
 385  0
     metaData.setName(name);
 386  0
   }
 387  
 
 388  
   private void addFiling(final ProjectdocMetaData metaData,
 389  
       final Element rootNode)
 390  
   {
 391  0
     final Element filing = rootNode.getChild("filing", getNs());
 392  0
     if (filing != null)
 393  
     {
 394  0
       addParents(metaData, filing);
 395  0
       addCategories(metaData, filing);
 396  0
       addTags(metaData, filing);
 397  0
       setSortKey(metaData, filing);
 398  
     }
 399  0
   }
 400  
 
 401  
   @SuppressWarnings(UNCHECKED)
 402  
   private void addParents(final ProjectdocMetaData metaData,
 403  
       final Element filing)
 404  
   {
 405  0
     final Element parentsElement = filing.getChild("parents", getNs());
 406  0
     if (parentsElement != null)
 407  
     {
 408  0
       final List<Element> parents =
 409  
           parentsElement.getChildren("parent", getNs());
 410  0
       for (final Element parent : parents)
 411  
       {
 412  0
         final String parentName = parent.getTextNormalize();
 413  0
         metaData.addParent(parentName);
 414  0
       }
 415  
     }
 416  0
   }
 417  
 
 418  
   @SuppressWarnings(UNCHECKED)
 419  
   private void addCategories(final ProjectdocMetaData metaData,
 420  
       final Element filing)
 421  
   {
 422  0
     final Element categoriesElement = filing.getChild("categories", getNs());
 423  0
     if (categoriesElement != null)
 424  
     {
 425  0
       final List<Element> categories =
 426  
           categoriesElement.getChildren("category", getNs());
 427  0
       for (final Element category : categories)
 428  
       {
 429  0
         final String categoryName = category.getTextNormalize();
 430  0
         metaData.addCategory(categoryName);
 431  0
       }
 432  
     }
 433  0
   }
 434  
 
 435  
   @SuppressWarnings(UNCHECKED)
 436  
   private void addTags(final ProjectdocMetaData metaData, final Element filing)
 437  
   {
 438  0
     final Element tagsElement = filing.getChild("tags", getNs());
 439  0
     if (tagsElement != null)
 440  
     {
 441  0
       final List<Element> tags = tagsElement.getChildren("tag", getNs());
 442  0
       for (final Element tag : tags)
 443  
       {
 444  0
         final String tagName = tag.getTextNormalize();
 445  0
         metaData.addTag(tagName);
 446  0
       }
 447  
     }
 448  0
   }
 449  
 
 450  
   private void setSortKey(final ProjectdocMetaData metaData,
 451  
       final Element filing)
 452  
   {
 453  0
     final String sortKey = filing.getChildTextNormalize("sortKey", getNs());
 454  0
     metaData.setSortKey(sortKey);
 455  0
   }
 456  
 
 457  
   @SuppressWarnings(UNCHECKED)
 458  
   private void addDescription(final ProjectdocMetaData metaData,
 459  
       final Element rootNode)
 460  
   {
 461  0
     final Element description = rootNode.getChild("description", getNs());
 462  0
     if (description != null)
 463  
     {
 464  0
       final Element audienceElement = description.getChild("audience", getNs());
 465  0
       if (audienceElement != null)
 466  
       {
 467  0
         final List<Element> members =
 468  
             audienceElement.getChildren("member", getNs());
 469  0
         for (final Element member : members)
 470  
         {
 471  0
           final String memberName = member.getTextNormalize();
 472  0
           metaData.addAudience(memberName);
 473  0
         }
 474  
       }
 475  
 
 476  0
       final Element shortDescriptionElement =
 477  
           description.getChild("shortDescription", getNs());
 478  0
       final String shortDescription = toString(shortDescriptionElement);
 479  0
       metaData.setShortDescription(shortDescription);
 480  
 
 481  0
       final Element summaryElement = description.getChild("summary", getNs());
 482  0
       final String summary = toString(summaryElement);
 483  0
       metaData.setSummary(summary);
 484  
 
 485  0
       final Element notesElement = description.getChild("notes", getNs());
 486  0
       final String notes = toString(notesElement);
 487  0
       metaData.addNote(notes);
 488  
     }
 489  0
   }
 490  
 
 491  
   /**
 492  
    * Returns the namespace of the elements parsed by this parser.
 493  
    *
 494  
    * @return the namespace of the elements parsed by this parser.
 495  
    */
 496  
   protected abstract Namespace getNs();
 497  
 
 498  
   private static String toString(final Element element)
 499  
   {
 500  0
     final XMLOutputter outp = new XMLOutputter();
 501  0
     final StringBuilder buffer = new StringBuilder(1024);
 502  
 
 503  0
     final List<?> children = element.getContent();
 504  0
     final String string = outp.outputString(children);
 505  0
     buffer.append(string);
 506  
 
 507  0
     return buffer.toString().trim();
 508  
   }
 509  
 
 510  
   // --- object basics --------------------------------------------------------
 511  
 
 512  
 }