db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhille...@apache.org
Subject svn commit: r1033940 - in /db/derby/code/trunk: build.xml java/build/org/apache/derbyBuild/ReleaseNotesTransformer.java
Date Thu, 11 Nov 2010 13:43:01 GMT
Author: rhillegas
Date: Thu Nov 11 13:43:01 2010
New Revision: 1033940

URL: http://svn.apache.org/viewvc?rev=1033940&view=rev
Log:
DERBY-4855: Add a new ant task and target for transforming the release notes into a download
page.

Added:
    db/derby/code/trunk/java/build/org/apache/derbyBuild/ReleaseNotesTransformer.java   (with
props)
Modified:
    db/derby/code/trunk/build.xml

Modified: db/derby/code/trunk/build.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/build.xml?rev=1033940&r1=1033939&r2=1033940&view=diff
==============================================================================
--- db/derby/code/trunk/build.xml (original)
+++ db/derby/code/trunk/build.xml Thu Nov 11 13:43:01 2010
@@ -2956,4 +2956,41 @@ you should not have to do this.
         <delete dir="${pruned.docs.dir}/pdf/${userguide}"/>
     </target>
 
+    <!-- Transform the release notes for publication on the website -->
+    <target name="transformrelnotes" depends="promptforreleaseid,promptforwebsite">
+
+        <property name="relnotes.src" value="${basedir}/RELEASE-NOTES.html" />
+
+        <taskdef
+          name="transformReleaseNotes"
+          classname="org.apache.derbyBuild.ReleaseNotesTransformer"
+          classpath="${out.dir}"
+        />
+        <transformReleaseNotes
+            inputFileName="${relnotes.src}"
+            outputFileName="${relnotes.target.dir}/release-${derby.release.id}.html"
+            releaseId="${derby.release.id}"
+        />
+
+    </target>
+
+    <!-- Prompt for and sanity check the root of your website client -->
+    <target name="promptforwebsite" depends="defineprompt">
+
+        <promptForProperty
+          propertyName="website.root"
+          prompt="Enter the root directory of your Derby website client (e.g. /Users/me/derby/site/trunk)
>  "
+        />
+        <property name="relnotes.target.dir" value="${website.root}/src/documentation/content/xdocs/releases"
/>
+
+        <fail message="Cannot transform the release notes. The web site client does not
look correct.">
+            <condition>
+              <not>
+                <available file="${relnotes.target.dir}"/>
+              </not>
+            </condition>
+        </fail>
+
+    </target>
+
 </project>

Added: db/derby/code/trunk/java/build/org/apache/derbyBuild/ReleaseNotesTransformer.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/build/org/apache/derbyBuild/ReleaseNotesTransformer.java?rev=1033940&view=auto
==============================================================================
--- db/derby/code/trunk/java/build/org/apache/derbyBuild/ReleaseNotesTransformer.java (added)
+++ db/derby/code/trunk/java/build/org/apache/derbyBuild/ReleaseNotesTransformer.java Thu
Nov 11 13:43:01 2010
@@ -0,0 +1,521 @@
+/*  Derby - Class org.apache.derbyBuild.ReleaseNotesTransformer
+
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+ */
+package org.apache.derbyBuild;
+
+import java.io.*;
+import java.util.*;
+import java.text.MessageFormat;
+import javax.xml.parsers.*;
+import javax.xml.transform.*;
+import javax.xml.transform.dom.*;
+import javax.xml.transform.stream.*;
+import org.w3c.dom.*;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * <p>
+ * This is an ant task which transforms the Derby release notes into a form
+ * which can be digested by the Forrest tool and published on the Derby
+ * download site. This involves the following transformations:
+ * </p>
+ *
+ * <ul>
+ * <li><b>Remove blockquotes</b> - Forrest silently swallows blockquoted
text.</li>
+ * <li><b>Remove TOC</b> - Forrest adds its own table of contents and transforms
the original TOC into a block of dead links.</li>
+ * <li><b>Remove mini TOC</b> - Forrest also transforms the mini TOC in
the Issues section into a block of dead links.</li>
+ * </ul>
+ */
+public class ReleaseNotesTransformer extends Task
+{
+    /////////////////////////////////////////////////////////////////////////
+    //
+    //  CONSTANTS
+    //
+    /////////////////////////////////////////////////////////////////////////
+
+    private static final String PREAMBLE =
+        "<!--\n" +
+        "  Licensed to the Apache Software Foundation (ASF) under one or more\n" +
+        "  contributor license agreements.  See the NOTICE file distributed with\n" +
+        "  this work for additional information regarding copyright ownership.\n" +
+        "  The ASF licenses this file to you under the Apache License, Version 2.0\n" +
+        "  (the \"License\"); you may not use this file except in compliance with\n" +
+        "  the License.  You may obtain a copy of the License at\n" +
+        "\n" +
+        "      http://www.apache.org/licenses/LICENSE-2.0\n" +
+        "\n" +
+        "  Unless required by applicable law or agreed to in writing, software\n" +
+        "  distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
+        "  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
+        "  See the License for the specific language governing permissions and\n" +
+        "  limitations under the License.\n" +
+        "-->\n" +
+        "<html>\n" +
+        "<title>Apache Derby {0} Release</title>\n" +
+        "<body>\n" +
+        "\n" +
+        "    <h1>Distributions</h1>\n" +
+        "    <p>Use the links below to download a distribution of Apache Derby. You
should <b>always</b> <a href=\"#Verifying Releases\">verify the integrity</a>\n"
+
+        "       of distribution files downloaded from a mirror.</p>\n" +
+        "\n" +
+        "<p>You are currently using <strong>[preferred]</strong>. If you
encounter a\n" +
+        "problem with this mirror, then please select another.  If all\n" +
+        "mirrors are failing, there are backup mirrors at the end of the list.\n" +
+        "See <a href=\"http://www.apache.org/mirrors/\">status</a> of mirrors.\n"
+
+        "</p>\n" +
+        "\n" +
+        "<form action=\"[location]\" method=\"get\" id=\"SelectMirror\">\n" +
+        "Other mirrors: <select name=\"Preferred\">\n" +
+        "<!--[if-any http] [for http]-->\n" +
+        "<option value=\"[http]\">[http]</option>\n" +
+        "<!--[end] [end]-->\n" +
+        "<!--[if-any ftp] [for ftp]-->\n" +
+        "<option value=\"[ftp]\">[ftp]</option>\n" +
+        "<!--[end] [end]-->\n" +
+        "<!--[if-any backup] [for backup]-->\n" +
+        "<option value=\"[backup]\">[backup] (backup)</option>\n" +
+        "<!--[end] [end]-->\n" +
+        "</select>\n" +
+        "<input type=\"submit\" value=\"Change\" />     \n" +
+        "</form>\n" +
+        "\n" +
+        "\n" +
+        "    <p>There are four different distributions:</p>\n" +
+        "    <ul>\n" +
+        "      <li>bin distribution - contains the documentation, javadoc, and jar
files for Derby.</li>\n" +
+        "      <li>lib distribution - contains only the jar files for Derby.</li>\n"
+
+        "      <li>lib-debug distribution - contains jar files for Derby with source
line numbers.</li>\n" +
+        "      <li>src distribution - contains the Derby source tree at the point which
the binaries were built.</li>\n" +
+        "    </ul>\n" +
+        "    <p> <a href=\"[preferred]/db/derby/db-derby-{0}/db-derby-{0}-bin.zip\">db-derby-{0}-bin.zip</a>
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-bin.zip.asc\">PGP</a>]
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-bin.zip.md5\">MD
  5</a>]<br/>\n" +
+        "    <a href=\"[preferred]/db/derby/db-derby-{0}/db-derby-{0}-bin.tar.gz\">db-derby-{0}-bin.tar.gz</a>
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-bin.tar.gz.asc\">PGP</a>]
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-bin.tar.gz.md5\">MD5</a>]</p>\n"
+
+        "    \n" +
+        "    <p><a href=\"[preferred]/db/derby/db-derby-{0}/db-derby-{0}-lib.zip\">db-derby-{0}-lib.zip</a>
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-lib.zip.asc\">PGP</a>]
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-lib.zip.md5\">MD5<
 /a>]<br/>\n" +
+        "    <a href=\"[preferred]/db/derby/db-derby-{0}/db-derby-{0}-lib.tar.gz\">db-derby-{0}-lib.tar.gz</a>
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-lib.tar.gz.asc\">PGP</a>]
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-lib.tar.gz.md5\">
MD5</a>]</p>\n" +
+        "    \n" +
+        "    <p><a href=\"[preferred]/db/derby/db-derby-{0}/db-derby-{0}-lib-debug.zip\">db-derby-{0}-lib-debug.zip</a>
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-lib-debug.zip.asc\">PGP</a>]
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-  lib-debug.zip.md5\">MD5</a>]<br/>\n"
+
+        "    <a href=\"[preferred]/db/derby/db-derby-{0}/db-derby-{0}-lib-debug.tar.gz\">db-derby-{0}-lib-debug.tar.gz</a>
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-lib-debug.tar.gz.asc\">PGP</a>]
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-10.6 .1.0-lib-debug.tar.gz.md5\">MD5</a>]</p>\n"
+
+        "\n" +
+        "    <p><a href=\"[preferred]/db/derby/db-derby-{0}/db-derby-{0}-src.zip\">db-derby-{0}-src.zip</a>
 [<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-src.zip.asc\">PGP</a>]
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-src.zip.md5\">MD5
 </a>]<br/>\n" +
+        "    <a href=\"[preferred]/db/derby/db-derby-{0}/db-derby-{0}-src.tar.gz\">db-derby-{0}-src.tar.gz</a>
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-src.tar.gz.asc\">PGP</a>]
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/db-derby-{0}-src.tar.gz.md5\">MD5</a>]
(Note that, due to long filenames, you will need gnu tar to unravel this tarball.)</p>\n"
+
+        "\n" +
+        "    <p>There are two separate Eclipse plugins for Derby:</p>\n" +
+        "    <ul>\n" +
+        "      <li>derby_core_plugin - provides the Derby jar files to other plugins
in Eclipse.</li>\n" +
+        "      <li>derby_ui_doc_plugin - provides an Apache Derby Nature in Eclipse
for easy database application development.</li>\n" +
+        "    </ul>\n" +
+        "    <p> <a href=\"[preferred]/db/derby/db-derby-{0}/derby_core_plugin_{1}.zip\">derby_core_plugin_{1}.zip</a>
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/derby_core_plugin_{1}.zip.asc\">PGP</a>]
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/derby_core_plugin_{1}.zip.md5\">MD5</a>]<br/>\n"
+
+        "    <a href=\"[preferred]/db/derby/db-derby-{0}/derby_ui_doc_plugin_1.1.2.zip\">derby_ui_doc_plugin_1.1.2.zip</a>
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/derby_ui_doc_plugin_1.1.2.zip.asc\">PGP</a>]
[<a href=\"http://www.apache.org/dist/db/derby/db-derby-{0}/derby_ui_doc_plugin_1.1.2.zip.md5\">MD5</a>]</p>\n"
+
+        "    <p>Please note: both plugins must be installed for full functionality.
For information on installing and using\n" +
+        "       the Derby plugins for Eclipse, please see the <a href=\"http://db.apache.org/derby/integrate/plugin_howto.html\">Using
the 10 Core and 1.1 UI Derby plug-ins</a> p  age.</p>\n";
+    
+
+
+    /////////////////////////////////////////////////////////////////////////
+    //
+    //  STATE
+    //
+    /////////////////////////////////////////////////////////////////////////
+
+    private DocumentBuilder _docBldr;
+    private Document _inputDoc;
+    private File _inputFile;
+    private File _outputFile;
+
+    private String _inputFileName;
+    private String _outputFileName;
+    private String _releaseID;
+
+    /////////////////////////////////////////////////////////////////////////
+    //
+    //  CONSTRUCTOR
+    //
+    /////////////////////////////////////////////////////////////////////////
+
+    public ReleaseNotesTransformer() throws Exception
+    {
+        _docBldr = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+    }
+    
+    /////////////////////////////////////////////////////////////////////////
+    //
+    //  ANT Task BEHAVIOR
+    //
+    /////////////////////////////////////////////////////////////////////////
+    
+    /**
+     * Ant accessor to set the name of the input file, the original release notes.
+     */
+    public void setInputFileName(String inputFileName) throws Exception
+    {
+        _inputFileName = inputFileName;
+        _inputFile = new File(_inputFileName);
+
+        println( "Reading from " + inputFileName + "..." );
+    }
+
+    /**
+     * Ant accessor to set the name of the generated output file
+     */
+    public void setOutputFileName(String outputFileName) throws Exception
+    {
+        _outputFileName = outputFileName;
+        _outputFile = new File(_outputFileName);
+
+        println( "Writing to " + outputFileName + "..." );
+    }
+
+    /**
+     * Ant accessor to set the release id.
+     */
+    public void setReleaseId(String releaseID) throws Exception
+    {
+        _releaseID = releaseID;
+
+        println( "Setting release id to " + _releaseID + "..." );
+    }
+
+    /**
+     * This is Ant's entry point into this task.
+     */
+    public void execute() throws BuildException
+    {
+        try {
+            transform();
+            printOutput();
+            postProcess();
+        }
+        catch (Throwable t) {
+            t.printStackTrace();
+
+            throw new BuildException("Error running ReleaseNotesTransformer: " + t.getMessage(),
t);
+        }
+    }
+
+    /////////////////////////////////////////////////////////////////////////
+    //
+    //  CORE BEHAVIOR
+    //
+    /////////////////////////////////////////////////////////////////////////
+
+    /**
+     *<p>
+     * This is the guts of the processing.
+     * </p>
+     */
+    private void transform() throws Exception
+    {
+        // this writes normalized text to the output file
+        normalizeText( _inputFile, _outputFile );
+
+        // correct text so that it is parseable and remove brackets which Forrest can't see
+        InputStream normalizedText = new FileInputStream( _outputFile );
+        _inputDoc = _docBldr.parse( normalizedText );
+        normalizedText.close();
+        
+        removeBlockquotes();
+        removeTopTOC();
+        removeIssuesTOC();
+    }
+
+    /**
+     * <p>
+     * Remove the blockquotes which hide text from Forrest.
+     * </p>
+     */
+    private void removeBlockquotes() throws Exception
+    {
+        Element root = _inputDoc.getDocumentElement();
+        HashSet<Element> replacedNodes = new HashSet<Element>();
+        String tag = "blockquote";
+
+        while ( true )
+        {
+            Element suspect = getFirstDescendant( root, tag );
+            if ( suspect == null ) { break; }
+
+            if ( replacedNodes.contains( suspect ) )
+            {
+                throw new Exception( "Stuck in a loop trying to strip '" + tag + "'" );
+            }
+            replacedNodes.add( suspect );
+
+            Element parent = (Element) suspect.getParentNode();
+            NodeList children = suspect.getChildNodes();
+
+            if ( children != null )
+            {
+                int childCount = children.getLength();
+
+                for ( int i = 0; i < childCount; i++ )
+                {
+                    Node oldChild = children.item( i );
+
+                    if ( oldChild != null )
+                    {
+                        Node newChild = oldChild.cloneNode( true );
+
+                        parent.insertBefore( newChild, suspect );
+                    }
+                }
+            }
+            parent.removeChild( suspect );
+        }   // end loop through suspects
+    }
+    
+    /**
+     * <p>
+     * Remove the top level table of contents. This is the first list in the document.
+     * </p>
+     */
+    private void removeTopTOC() throws Exception
+    {
+        removeFirstList( _inputDoc.getDocumentElement() );
+    }
+
+    /**
+     * <p>
+     * Remove the table of contents of the Issues section. This is the first list in that
section.
+     * </p>
+     */
+    private void removeIssuesTOC() throws Exception
+    {
+        Element issuesHeader = findHeader( 2, "Issues" );
+
+        if ( issuesHeader != null )
+        {
+            // now look for the first list that follows
+
+            NodeList allLists = _inputDoc.getDocumentElement().getElementsByTagName( "ul"
);
+
+            if ( allLists == null ) { return; }
+
+            int count = allLists.getLength();
+
+            for ( int i = 0; i < count; i++ )
+            {
+                Node nextList = allLists.item( i );
+                
+                if ( issuesHeader.compareDocumentPosition( nextList ) == Node.DOCUMENT_POSITION_FOLLOWING
)
+                {
+                    nextList.getParentNode().removeChild( nextList );
+                    break;
+                }
+            }
+
+        }
+    }
+
+    /**
+     * <p>
+     * Remove the first list under an element.
+     * </p>
+     */
+    private void removeFirstList( Element root ) throws Exception
+    {
+        Element listElement = getFirstDescendant( root, "ul" );
+
+        if ( listElement != null )
+        {
+            listElement.getParentNode().removeChild( listElement );
+        }
+    }
+
+    /**
+     * <p>
+     * Find the header element with this given name.
+     * </p>
+     */
+    private Element findHeader( int headerLevel, String headerTitle ) throws Exception
+    {
+        Element root = _inputDoc.getDocumentElement();
+        String headerTag = "h" +  headerLevel;
+        NodeList headers = root.getElementsByTagName( headerTag );
+
+        if ( headers == null ) { return null; }
+
+        int count = headers.getLength();
+
+        for ( int i = 0; i < count; i++ )
+        {
+            Node nextHeader = headers.item( i );
+            String title = nextHeader.getTextContent().trim();
+
+            if ( headerTitle.equals( title ) ) { return (Element) nextHeader; }
+        }
+
+        return null;
+    }
+
+    
+    /////////////////////////////////////////////////////////////////////////
+    //
+    //  MINIONS
+    //
+    /////////////////////////////////////////////////////////////////////////
+
+    private Element getFirstDescendant( Element ancestor, String tagName )
+    {
+        NodeList nl = ancestor.getElementsByTagName( tagName );
+
+        if ( nl == null ) { return null; }
+        if ( nl.getLength() == 0 ) { return null; }
+
+        return (Element) nl.item( 0 );
+    }
+    
+    /**
+     * Adjust input text to remove junk which confuses the xml parser and/or Forrest.
+     * Temporarily writes the adjusted text to the output file.
+     */
+    private void normalizeText( File inputFile, File outputFile ) throws Exception
+    {
+        String rawString = readFileIntoString( inputFile );
+
+        // The Transformer which wrote the original release notes managed to turn <br/>
into <br>
+        // and <hr/> into <hr>. Fix this.
+        rawString = fullReplaceToken( rawString, "<br>", "<br/>" );
+        rawString = fullReplaceToken( rawString, "<hr>", "<hr/>" );
+
+        // Forrest doesn't like square brackets and swallows the bracketed content
+        rawString.replace( '[', '(' );
+        rawString.replace( ']', ')' );
+
+        FileWriter fileWriter = new FileWriter( outputFile );
+        fileWriter.append( rawString );
+        fileWriter.flush();
+        fileWriter.close();
+    }
+    private String fullReplaceToken( String rawString, String token, String replacement )
+    {
+        rawString = replaceToken( rawString, token.toLowerCase(), replacement );
+        rawString = replaceToken( rawString, token.toUpperCase(), replacement );
+        
+        return rawString;
+    }
+    private String replaceToken( String rawString, String token, String replacement )
+    {
+        StringWriter output = new StringWriter();
+        int rawLength = rawString.length();
+        int tokenLength = token.length();
+        int start = 0;
+
+        while ( true )
+        {
+            int idx = rawString.indexOf( token, start );
+            if ( idx < 0 ) { break; }
+
+            output.append( rawString.substring( start, idx ) );
+            output.append( replacement );
+            start = idx + tokenLength;
+        }
+
+        if ( start < rawLength )
+        {
+            output.append( rawString.substring( start, rawLength ) );
+        }
+
+        return output.toString();
+    }
+    
+    /**
+     * Print the generated output document to the output file.
+     */
+    private void printOutput() throws Exception
+    {
+        Source source = new DOMSource(_inputDoc);
+
+        Result result = new StreamResult(_outputFile);
+        Transformer transformer = TransformerFactory.newInstance().newTransformer();
+
+        transformer.transform(source, result);
+    }
+
+    /**
+     * <p>
+     * Post-process the output:
+     * </p>
+     *
+     * <ul>
+     * <li>Add preamble to the head of the file.</li>
+     * </ul>
+     */
+    private void postProcess()
+        throws Exception
+    {
+        String shortReleaseID = _releaseID.substring( 0, _releaseID.lastIndexOf( "." ) );
+        String preamble = MessageFormat.format( PREAMBLE, _releaseID, shortReleaseID );
+        String contents = readFileIntoString( _outputFile );
+        String firstHeader = "<h1>";
+        int cutIdx = contents.indexOf( firstHeader );
+        String result = preamble + contents.substring( cutIdx );
+
+        writeStringIntoFile( result, _outputFile );
+    }
+    
+    /**
+     * Print a line of text to the console.
+     */
+    private void println(String text)
+    {
+        log(text, Project.MSG_WARN);
+    }
+
+    /**
+     * <p>
+     * Read a file and return the entire contents as a single String.
+     * </p>
+     */
+    private String readFileIntoString( File inputFile ) throws Exception
+    {
+        FileReader fileReader = new FileReader( inputFile );
+        StringWriter stringWriter = new StringWriter();
+
+        while( true )
+        {
+            int nextChar = fileReader.read();
+            if ( nextChar < 0 ) { break; }
+
+            stringWriter.append( (char) nextChar );
+        }
+
+        String rawString = stringWriter.toString();
+
+        return rawString;
+    }
+
+    /**
+     * <p>
+     * Write a string into a file.
+     * </p>
+     */
+    private void writeStringIntoFile( String rawString, File outputFile )
+        throws Exception
+    {
+        PrintWriter writer = new PrintWriter( outputFile, "UTF-8" );
+
+        writer.println( rawString );
+        writer.flush();
+        writer.close();
+    }
+
+}

Propchange: db/derby/code/trunk/java/build/org/apache/derbyBuild/ReleaseNotesTransformer.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message