Coverage Report - org.webmacro.directive.IncludeDirective
 
Classes in this File Line Coverage Branch Coverage Complexity
IncludeDirective
57%
71/124
62%
43/69
6.385
 
 1  
 /*
 2  
  * Copyright (C) 1998-2000 Semiotek Inc.  All Rights Reserved.
 3  
  *
 4  
  * Redistribution and use in source and binary forms, with or without
 5  
  * modification, are permitted under the terms of either of the following
 6  
  * Open Source licenses:
 7  
  *
 8  
  * The GNU General Public License, version 2, or any later version, as
 9  
  * published by the Free Software Foundation
 10  
  * (http://www.fsf.org/copyleft/gpl.html);
 11  
  *
 12  
  *  or
 13  
  *
 14  
  * The Semiotek Public License (http://webmacro.org/LICENSE.)
 15  
  *
 16  
  * This software is provided "as is", with NO WARRANTY, not even the
 17  
  * implied warranties of fitness to purpose, or merchantability. You
 18  
  * assume all risks and liabilities associated with its use.
 19  
  *
 20  
  * See www.webmacro.org for more information on the WebMacro project.
 21  
  */
 22  
 
 23  
 package org.webmacro.directive;
 24  
 
 25  
 import java.io.IOException;
 26  
 import java.io.InputStream;
 27  
 import java.net.URL;
 28  
 import java.net.URLConnection;
 29  
 import java.util.StringTokenizer;
 30  
 
 31  
 import org.slf4j.Logger;
 32  
 
 33  
 import org.webmacro.Broker;
 34  
 import org.webmacro.Context;
 35  
 import org.webmacro.FastWriter;
 36  
 import org.webmacro.Macro;
 37  
 import org.webmacro.NotFoundException;
 38  
 import org.webmacro.PropertyException;
 39  
 import org.webmacro.ResourceException;
 40  
 import org.webmacro.Template;
 41  
 import org.webmacro.TemplateVisitor;
 42  
 import org.webmacro.engine.BuildContext;
 43  
 import org.webmacro.engine.BuildException;
 44  
 
 45  
 /**
 46  
  * IncludeDirective allows you to include other text files or Templates into
 47  
  * the current Template.
 48  
  * <p>
 49  
  * Syntax:<pre>
 50  
  *    #include [as text|template|macro] quoted-string-variable
 51  
  *</pre>
 52  
  *
 53  
  * <code>IncludeDirective</code> has 4 modes of operation which are detailed
 54  
  * below.<p>
 55  
  *
 56  
  * Please note that #include is now the preferred way to include files or
 57  
  * templates.  Although you can still use #parse (in the same manner as
 58  
  * described below), its use has been deprecated.<p>
 59  
  *
 60  
  * <b>Including as text</b><br>
 61  
  * Including a file as text causes the raw bytes of the file to be included
 62  
  * in the current template.  The file is <b>not</b> considered to be a Template,
 63  
  * and is not parsed by WebMacro.  This allows you to include external files
 64  
  * that contain javascript or stylesheets without fear of mangling by WebMacro's
 65  
  * parser.<p>
 66  
  *
 67  
  * Files included as text are found using the default <code>URLProvider</code>.
 68  
  * If the URLProvider cannot find the file, the Broker is asked to find it.
 69  
  * This allows you to include files from any of the following places:
 70  
  * <li>other websites
 71  
  * <li>local filesystem
 72  
  * <li>active classpath<p>
 73  
  *
 74  
  * Files included as text are located once.  Changes to the included file
 75  
  * will not be found by WebMacro!<p>
 76  
  *
 77  
  * Examples:<pre>
 78  
  *
 79  
  *   // include your system password file (not a good idea! :)
 80  
  *   #include as text "/etc/password"
 81  
  *
 82  
  *   // include the WebMacro homepage
 83  
  *   #include as text "http://www.webmacro.org"
 84  
  *
 85  
  *   // inlude a file that is in your classpath
 86  
  *   #include as text "somefile.txt"
 87  
  *
 88  
  * </pre><br>
 89  
  *
 90  
  * <b>Including as template</b><br>
 91  
  * Including a file as template causes the file to be parsed and evaluated
 92  
  * by WebMacro using the current <code>Context</code>.  The evaluated output of
 93  
  * the Template is included in your main template.<p>
 94  
  *
 95  
  * Please note that files included "as template" that contains #macro's <b>DO NOT</b>
 96  
  * make their #macro's available to the outer template.  See "Including as macro" below
 97  
  * for this.<p>
 98  
  *
 99  
  * Files included as template are found in your <code>TemplatePath</code>.
 100  
  * This path, if not explicitly set in <code>WebMacro.properties</code>, defaults
 101  
  * to the system classpath (standalone and JSDK 1.0), or the "web-app" directory
 102  
  * (JSDK 2.x).<p>
 103  
  *
 104  
  * Files included as templates are located and conditionally reloaded/parsed when
 105  
  * they change.<p>
 106  
  *
 107  
  * Examples:<pre>
 108  
  *
 109  
  *   // include a global header template
 110  
  *   #include as template "header.wm"
 111  
  *
 112  
  *   // include a header template from a subdirectory of your TemplatePath
 113  
  *   #include as template "common/header.wm"
 114  
  *
 115  
  *   // include a header template using an absolute path in your TemplatePath
 116  
  *   #include as template "/mysite/common/header.wm"
 117  
  *
 118  
  * </pre><br>
 119  
  *
 120  
  * <b>Including as macro</b><br>
 121  
  * Including a file as a macro is similiar to the above, except that #macro's
 122  
  * defined in the included file <b>are</b> made available to the outer template.
 123  
  * "Macro" templates are found using the same TemplatePath settings as
 124  
  * "#include as template".<p>
 125  
  *
 126  
  * Files included as macros are located once.  Changes to the included template
 127  
  * will not be found by WebMacro!<p>
 128  
  *
 129  
  * Exapmles:<pre>
 130  
  *
 131  
  *   // include a template with macros
 132  
  *   #include as macro "my_macros.wm"
 133  
  *
 134  
  * </pre><br>
 135  
  *
 136  
  *
 137  
  * <b>Including without qualification</b><br></a>
 138  
  * Including a file without first qualifing it as a <i>text</i> file or
 139  
  * <i>template</i> causes <code>IncludeDirective</code> to make some educated
 140  
  * guesses about what type of file it is.<p>
 141  
  *
 142  
  * If the filename contains <code>://</code>, it is assumed to be a URL and is
 143  
  * treated as if it were a <code>text</code> file.<p>
 144  
  *
 145  
  * Else if the filename ends with any of the configured template file extensions
 146  
  * (see below), it is assumed to be a template.<p>
 147  
  *
 148  
  * Else it is assumed to be <code>text</code>.<p>
 149  
  *
 150  
  * Examples:<pre>
 151  
  *
 152  
  *    // include homepage of WebMacro -- assumed to be "as text"
 153  
  *    #include "http://www.webmacro.org/"
 154  
  *
 155  
  *    // include a template -- assumed to be "as template"
 156  
  *    #include "header.wm"
 157  
  *
 158  
  *    // include a text file from local filesystem -- assumed to be "as text"
 159  
  *    #include "somefile.txt"
 160  
  *
 161  
  * </pre>
 162  
  *
 163  
  *
 164  
  * <b><code>WebMacro.properties</code> Configuration Options</b><br>
 165  
  *
 166  
  * <code>include.TemplateExtensions</code>: list of file extensions<br>
 167  
  * This list of file extensions is used to determine the file type of an
 168  
  * unqualified #include'd file.<p>
 169  
  *
 170  
  * The default set of extensions are: <code>wm, wmt, tml</code>
 171  
  *
 172  
  * @author <a href=mailto:ebr@tcdi.com>Eric B. Ridge</a>
 173  
  * @since the beginning, but consolidated with #parse post 0.97
 174  
  *
 175  
  */
 176  891
 public class IncludeDirective extends Directive
 177  
 {
 178  
 
 179  
     /** The file to include is a Template. */
 180  
     public static final int TYPE_TEMPLATE = 0;
 181  
     /** The file to include as a Template containing #macro's. */
 182  
     public static final int TYPE_MACRO = 1;
 183  
     /** The file to include is a static file. */
 184  
     public static final int TYPE_TEXT = 2;
 185  
     /** The file to include is unknown. */
 186  
     public static final int TYPE_DYNAMIC = 3;
 187  
 
 188  
 
 189  
     private static final int PARSE_AS_K = 1;
 190  
     private static final int PARSE_TEMPLATE_K = 2;
 191  
     private static final int PARSE_TEXT_K = 3;
 192  
     private static final int PARSE_MACRO_K = 4;
 193  
     private static final int PARSE_FILENAME = 5;
 194  
 
 195  40
     private static final ArgDescriptor[] _args = new ArgDescriptor[]{
 196  
         new OptionalGroup(4),
 197  
         new KeywordArg(PARSE_AS_K, "as"),
 198  
         new OptionalGroup(1),
 199  
         new KeywordArg(PARSE_TEMPLATE_K, "template"),
 200  
         new OptionalGroup(1),
 201  
         new KeywordArg(PARSE_TEXT_K, "text"),
 202  
         new OptionalGroup(1),
 203  
         new KeywordArg(PARSE_MACRO_K, "macro"),
 204  
         new QuotedStringArg(PARSE_FILENAME),
 205  
     };
 206  40
     private static final DirectiveDescriptor _desc = new DirectiveDescriptor("include", null, _args, null);
 207  
 
 208  
     public static DirectiveDescriptor getDescriptor ()
 209  
     {
 210  126
         return _desc;
 211  
     }
 212  
 
 213  
 
 214  
     //
 215  
     // these values are customized for this directive during build()
 216  
     //
 217  
 
 218  
     /** Place holder for the TemplateExtensions configuration key name. */
 219  891
     private String TEMPLATE_EXTENSIONS_NAME = ".TemplateExtensions";
 220  
 
 221  
 
 222  
     /** Logging can be good */
 223  
     protected Logger _log;
 224  
     /** The included file type.  
 225  
      * One of TYPE_TEMPLATE, TYPE_STATIC, TYPE_MACRO, or TYPE_DYNAMIC. */
 226  
     protected int _type;
 227  
     /** The filename as a Macro, if the filename arg is a Macro. */
 228  
     protected Macro _macFilename;
 229  
     /**
 230  
      * The filename as a String, if it is something we can determine during
 231  
      * build().
 232  
      */
 233  
     protected String _strFilename;
 234  
     /**
 235  
      * The name given to the directive by webmacro configuration.  Used in
 236  
      * conjuction with the <code>StrictCompatibility</code>  configuration
 237  
      * and to make determinitaions on how the #included file should be included.
 238  
      */
 239  
     protected String _directiveName;
 240  
 
 241  
     /**
 242  
      * Build this use of the directive.<p>
 243  
      *
 244  
      * The specified file to include is included during <b>build/parse</b>
 245  
      * time if:<br>
 246  
      *    1) The specified filename is a String, not a $Variable; and<br>
 247  
      *    2) The "as" keyword is <b>"macro"</b>; or<br>
 248  
      *    3) if the <code>Lazy</code> configuration option is set for
 249  
      *       this directive.<p>
 250  
      *
 251  
      * Otherwise, template is found and including during runtime evaluation
 252  
      * of this directive.
 253  
      */
 254  
     public final Object build (DirectiveBuilder builder, BuildContext bc) 
 255  
         throws BuildException
 256  
     {
 257  891
         Broker broker = bc.getBroker();
 258  891
         _log = bc.getLog("IncludeDirective");
 259  
         // build configuration key names, since they're based
 260  
         // on the configured name of this directive
 261  891
         _directiveName = builder.getName();
 262  891
         TEMPLATE_EXTENSIONS_NAME = _directiveName + TEMPLATE_EXTENSIONS_NAME;
 263  
 
 264  
         // determine what type of file we need to deal with
 265  891
         if (builder.getArg(PARSE_TEXT_K, bc) != null)
 266  
         {
 267  3
             _type = TYPE_TEXT;
 268  
         }
 269  888
         else if (builder.getArg(PARSE_TEMPLATE_K, bc) != null)
 270  
         {
 271  2
             _type = TYPE_TEMPLATE;
 272  
         }
 273  886
         else if (builder.getArg(PARSE_MACRO_K, bc) != null)
 274  
         {
 275  19
             _type = TYPE_MACRO;
 276  
         }
 277  
         else
 278  
         {
 279  867
             _type = TYPE_DYNAMIC;
 280  
         }
 281  
 
 282  
 
 283  
         // if the filename passed to us was a Macro (needs to be evaluated later)
 284  
         // then store it as _macFilename.  Otherwise, assume it's a String
 285  
         // and we'll just use that string as the filename
 286  891
         Object o = builder.getArg(PARSE_FILENAME, bc);
 287  891
         if (o instanceof Macro)
 288  
         {
 289  527
             if (_type == TYPE_TEXT || _type == TYPE_MACRO)
 290  
             {
 291  0
                 _log.warn("Included a 'static' file type using a dynamic filename. "
 292  
                         + "File will be included, but any included #macro's will not at " + bc.getCurrentLocation());
 293  
             }
 294  527
             _macFilename = (Macro) o;
 295  
         }
 296  
         else
 297  
         {
 298  364
             _strFilename = o.toString();
 299  364
             if (_strFilename == null || _strFilename.length() == 0)
 300  0
                 throw makeBuildException("Filename cannot be null or empty");
 301  
 
 302  
 
 303  364
             if (_type == TYPE_TEXT)
 304  
             {
 305  
                 // we're a static type, need to
 306  
                 // include the file (by returning it) now,
 307  
                 // during build time
 308  
                 try
 309  
                 {
 310  3
                     return getThingToInclude(broker, _type, _strFilename);
 311  
                 }
 312  0
                 catch (Exception e)
 313  
                 {
 314  0
                     throw makeBuildException("Unable to include as text", e);
 315  
                 }
 316  
             }
 317  361
             else if (_type == TYPE_MACRO)
 318  
             {
 319  
                 // we're a template type.  ned to get the template (already parsed)
 320  
                 // and merge its macros into our build context.
 321  
                 // then we return the template so its contents can also be included
 322  19
                 Template t = null;
 323  
                 try
 324  
                 {
 325  19
                     t = getTemplate(broker, _strFilename);
 326  19
                     bc.mergeConstants(t);
 327  
                 }
 328  0
                 catch (Exception e)
 329  
                 {
 330  0
                     throw makeBuildException("Unable to include as macro", e);
 331  19
                 }
 332  
                 try {
 333  19
                     return t.evaluateAsString(bc);
 334  
                 }
 335  0
                 catch (PropertyException e) {
 336  0
                     return "";
 337  
                 }
 338  
             }
 339  342
             else if (_type == TYPE_DYNAMIC)
 340  
             {
 341  
                 // being dynamic means we need to guess the
 342  
                 // file type based on the file's extension
 343  
                 // and take care of finding the file during runtime
 344  340
                 _type = guessType(broker, _strFilename);
 345  
             }
 346  
 
 347  
         }
 348  
 
 349  
         // we are configured to be lazy, or we couldn't determine the filename
 350  
         // during the build() process (b/c it is a Macro)
 351  869
         return this;
 352  
     }
 353  
 
 354  
     /**
 355  
      * Write out the included file to the specified FastWriter.  If the
 356  
      * included file is actually a template, it is evaluated against the
 357  
      * <code>context</code> parameter before being written to the FastWriter
 358  
      */
 359  
     public void write (FastWriter out, Context context) 
 360  
         throws PropertyException, IOException
 361  
     {
 362  1814
         Broker broker = context.getBroker();
 363  
 
 364  
         // the filename arg passed to us was a Macro, so
 365  
         // evaluate and check it now
 366  1814
         if (_macFilename != null)
 367  
         {
 368  1472
             _strFilename = _macFilename.evaluate(context).toString();
 369  1472
             if (_strFilename == null || _strFilename.length() == 0)
 370  
             {
 371  0
                 throw makePropertyException("Filename cannot be null or empty");
 372  
             }
 373  
         }
 374  
 
 375  1814
         if (_log.isDebugEnabled() && context.getCurrentLocation().indexOf(_strFilename) > -1)
 376  
         {
 377  
             // when in debug mode, output a warning if a template tries to include itself
 378  
             // there are situations where this is desired, but it's good to make
 379  
             // the user aware of what they're doing
 380  0
             _log.warn(context.getCurrentLocation() + " includes itself.");
 381  
         }
 382  
 
 383  
         // this should only be true if StrictCompatibility is set to false
 384  
         // and "as <something>" wasn't specified in the arg list
 385  1814
         if (_type == TYPE_DYNAMIC)
 386  527
             _type = guessType(broker, _strFilename);
 387  
 
 388  1814
         _log.debug("Including '" + _strFilename + "' as "
 389  
                     + ((_type == TYPE_MACRO) ? "MACRO"
 390  
                     : (_type == TYPE_TEMPLATE) ? "TEMPLATE"
 391  
                     : (_type == TYPE_TEXT) ? "TEXT"
 392  
                     : "UNKNOWN.  Throwing exception"));
 393  
 
 394  1814
         Object toInclude = getThingToInclude(broker, _type, _strFilename);
 395  1814
         switch (_type)
 396  
         {
 397  
             case TYPE_MACRO:
 398  
                 // during runtime evaluation of a template,
 399  
                 // a TYPE_MACRO doesn't really work as expected.
 400  
                 // we logged a warning above in build(), but
 401  
                 // we still need to write it out as a template
 402  
                 // so just fall through
 403  
             case TYPE_TEMPLATE:
 404  1791
                 ((Template) toInclude).write(out, context);
 405  1791
                 break;
 406  
 
 407  
             case TYPE_TEXT:
 408  
                 // static types are strings
 409  23
                 out.write(toInclude.toString());
 410  23
                 break;
 411  
 
 412  
             default:
 413  
                 // should never happen
 414  0
                 throw makePropertyException("Unrecognized file type: " + _type);
 415  
         }
 416  1814
     }
 417  
 
 418  
     /**
 419  
      * Get an array of Template file extensions we should use, if type==dynamic,
 420  
      * to decide if the specified file is a template or not.
 421  
      */
 422  
     protected String[] getTemplateExtensions (Broker b)
 423  
     {
 424  866
         String[] ret = (String[]) b.getBrokerLocal(TEMPLATE_EXTENSIONS_NAME);
 425  
 
 426  866
         if (ret == null)
 427  
         {
 428  4
             String prop = b.getSettings().getSetting(TEMPLATE_EXTENSIONS_NAME, "wm");
 429  4
             StringTokenizer st = new StringTokenizer(prop, ",; ");
 430  4
             ret = new String[st.countTokens()];
 431  4
             int x = 0;
 432  24
             while (st.hasMoreElements())
 433  
             {
 434  20
                 ret[x] = st.nextToken();
 435  20
                 x++;
 436  
             }
 437  
 
 438  4
             b.setBrokerLocal(TEMPLATE_EXTENSIONS_NAME, ret);
 439  
         }
 440  
 
 441  866
         return ret;
 442  
     }
 443  
 
 444  
     /**
 445  
      * If the filename contains <i>://</i> assume it's a file b/c it's probably
 446  
      * a url.<p>
 447  
      *
 448  
      * If the filename ends with any of the configured
 449  
      * <code>ParseDirective.TemplateExtensions</code>, assume it's a template.<p>
 450  
      *
 451  
      * Otherwise, it must be a file
 452  
      */
 453  
     protected int guessType (Broker b, String filename)
 454  
     {
 455  867
         if (filename.indexOf("://") > -1)
 456  
         {
 457  1
             return TYPE_TEXT;
 458  
         }
 459  
         else
 460  
         {
 461  866
             String[] extensions = getTemplateExtensions(b);
 462  976
             for (int x = 0; x < extensions.length; x++)
 463  
             {
 464  954
                 if (filename.endsWith(extensions[x]))
 465  844
                     return TYPE_TEMPLATE;
 466  
             }
 467  
         }
 468  
 
 469  22
         return TYPE_TEXT;
 470  
     }
 471  
 
 472  
     /**
 473  
      * Get the template or file that the user wants to include, based on the
 474  
      * specified type.  This method does not know how to get a file whose type.
 475  
      * is "TYPE_DYNAMIC".
 476  
      */
 477  
     protected Object getThingToInclude (Broker b, int type, String filename) throws PropertyException
 478  
     {
 479  1817
         switch (type)
 480  
         {
 481  
             case TYPE_TEMPLATE:
 482  
                 // wants to include a template
 483  1791
                 return getTemplate(b, filename);
 484  
 
 485  
             case TYPE_MACRO:
 486  
                 // wants to include a template
 487  0
                 return getTemplate(b, filename);
 488  
 
 489  
             case TYPE_TEXT:
 490  
                 // wants to include a static file
 491  26
                 return getFile(b, filename);
 492  
 
 493  
             case TYPE_DYNAMIC:
 494  
                 // this should never happen
 495  0
                 throw makePropertyException("Internal Error.  Never guessed file type");
 496  
 
 497  
             default:
 498  
                 // default case should never happen b/c we take care of this
 499  
                 // during build()
 500  0
                 throw makePropertyException("Internal Error.  Unrecognized file type: " + type);
 501  
         }
 502  
     }
 503  
 
 504  
     /**
 505  
      * Get a Template via the "template" provider known by the specified broker.
 506  
      */
 507  
     protected Template getTemplate (Broker b, String name) throws PropertyException
 508  
     {
 509  
         try
 510  
         {
 511  1810
             return (Template) b.get("template", name);
 512  
         }
 513  0
         catch (NotFoundException nfe)
 514  
         {
 515  0
             throw makePropertyException("Not found by template provider");
 516  
         }
 517  0
         catch (ResourceException re)
 518  
         {
 519  0
             throw makePropertyException("Unable to get template", re);
 520  
         }
 521  0
         catch (Exception e)
 522  
         {
 523  0
             throw makePropertyException("Unexpected exception while getting template");
 524  
         }
 525  
     }
 526  
 
 527  
     /**
 528  
      * Get the contents of a file (local file or url) via the "url" provider
 529  
      * known by the specified broker.  If the url provider can't find it
 530  
      * we check the Broker (Broker.getResource).
 531  
      */
 532  
     protected String getFile (Broker b, String name) throws PropertyException
 533  
     {
 534  
         try
 535  
         {
 536  
             // first, ask the URL provider (if we have one) to find the file for us
 537  26
             return b.get("url", name).toString();
 538  
         }
 539  0
         catch (Exception e)
 540  
         {
 541  
             // for whatever reason, the URL provider couldn't find the file
 542  
             // (maybe we don't have a URL provider?).  No matter, directly
 543  
             // ask the Broker to load it as a resource
 544  0
             URL url = null;
 545  
 
 546  
             try
 547  
             {
 548  0
                 url = b.getResource(name);
 549  0
                 if (url == null) // doh!  the Broker couldn't find it either.  Guess it doesn't exist
 550  0
                     throw makePropertyException("Resource not found by URL provider or Broker");
 551  
 
 552  
                 // open a URLConnection...
 553  0
                 URLConnection conn = url.openConnection();
 554  0
                 StringBuffer sb = new StringBuffer();
 555  0
                 InputStream in = conn.getInputStream();
 556  0
                 String enc = conn.getContentEncoding();
 557  0
                 if (enc == null)
 558  0
                     enc = b.getSetting("TemplateEncoding");
 559  
 
 560  
                 // ...and stream the contents of the URL into a String
 561  0
                 int cnt = 0;
 562  0
                 byte[] buff = new byte[4096];
 563  0
                 while ((cnt = in.read(buff)) > 0)
 564  
                 {
 565  0
                     sb.append(new String(buff, 0, cnt, enc));
 566  
                 }
 567  0
                 in.close();
 568  
 
 569  
                 // return the string form of the resource.
 570  
                 // This is what will be included in the template
 571  0
                 return sb.toString();
 572  
             }
 573  0
             catch (IOException ioe)
 574  
             {
 575  0
                 throw makePropertyException("Error streaming file from: " + url, ioe);
 576  
             }
 577  
         }
 578  
     }
 579  
 
 580  
     public void accept (TemplateVisitor v)
 581  
     {
 582  0
         v.beginDirective(_desc.name);
 583  0
         v.visitDirectiveArg("IncludeDirective", _strFilename);
 584  0
         v.endDirective();
 585  0
     }
 586  
 
 587  
     //
 588  
     // helper methods for throwing exceptions
 589  
     //
 590  
 
 591  
     private BuildException makeBuildException (String message)
 592  
     {
 593  0
         return makeBuildException(message, null);
 594  
     }
 595  
 
 596  
     private BuildException makeBuildException (String message, Exception cause)
 597  
     {
 598  0
         message = "#" + _directiveName + " " + _strFilename + ": " + message;
 599  0
         if (cause != null)
 600  0
             return new BuildException(message, cause);
 601  
         else
 602  0
             return new BuildException(message);
 603  
     }
 604  
 
 605  
     private PropertyException makePropertyException (String message)
 606  
     {
 607  0
         return makePropertyException(message, null);
 608  
     }
 609  
 
 610  
     private PropertyException makePropertyException (String message, Exception cause)
 611  
     {
 612  0
         message = "#" + _directiveName + " " + _strFilename + ": " + message;
 613  0
         if (cause != null)
 614  0
             return new PropertyException(message, cause);
 615  
         else
 616  0
             return new PropertyException(message);
 617  
     }
 618  
 }
 619