giacomo     01/06/12 07:51:18

  Modified:    webapp   sitemap.xmap
  Added:       src/org/apache/cocoon/generation StreamGenerator.java
               src/org/apache/cocoon/util PostInputStream.java
               webapp/docs/samples/stream OrderPage.xml ReadMeAdd.txt
                        telnet.txt
  Log:
  Added StreamGenerator and samples
  Submitted by: kingadziembowska <[EMAIL PROTECTED]>
  
  Revision  Changes    Path
  1.12      +15 -25    xml-cocoon2/webapp/sitemap.xmap
  
  Index: sitemap.xmap
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/webapp/sitemap.xmap,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -u -r1.11 -r1.12
  --- sitemap.xmap      2001/05/29 20:11:38     1.11
  +++ sitemap.xmap      2001/06/12 14:51:16     1.12
  @@ -17,6 +17,7 @@
      <map:generator  name="velocity"    
src="org.apache.cocoon.generation.VelocityGenerator"/>
      <map:generator  name="html"        
src="org.apache.cocoon.generation.HTMLGenerator" label="content"/>
      <map:generator  name="jsp"         
src="org.apache.cocoon.generation.JspGenerator"/>
  +   <map:generator  name="stream"      
src="org.apache.cocoon.generation.StreamGenerator"/>
     </map:generators>
   
     <map:transformers default="xslt">
  @@ -346,31 +347,6 @@
       <map:read src="docs/samples/{1}.png" mime-type="image/png"/>
      </map:match>
   
  -   <!--
  -    This pipeline is responsable for generating the images for the navigation
  -    bar. It uses the navimage.xsp to generate an SVG document which contains
  -    the text for the image from the request URI passed as a parameter. The
  -    requested language is evaluated by the LangSelect action. The text
  -    represents a key into the i18n dictionary for all navigation items. At
  -    the end the generated SVG is serialized as a PNG image.
  -    This pipeline is fully cachable and thus an image depends only on
  -    the text id and the laguage used and is generated once for each combination.
  -   -->
  -   <map:match type="regexp" pattern="i18n/images/(.*)_(.*)_(.*).png">
  -    <map:generate type="serverpages" src="docs/samples/i18n/navimages.xsp">
  -     <map:parameter name="text" value="{1}"/>
  -     <map:parameter name="lang" value="{2}"/>
  -     <map:parameter name="kind" value="{3}"/>
  -    </map:generate>
  -    <map:transform type="i18n" src="docs/samples/i18n/nav_trans.xml">
  -     <map:parameter name="lang" value="{2}"/>
  -     <map:parameter name="default_lang" value="en"/>
  -     <map:parameter name="available_lang_1" value="en"/>
  -     <map:parameter name="available_lang_2" value="de"/>
  -    </map:transform>
  -    <map:serialize type="svg2png"/>
  -   </map:match>
  -
      <!-- =========================== Dynamic ================================ -->
      <map:match pattern="xsp/*">
       <map:generate type="serverpages" src="docs/samples/xsp/{1}.xsp"/>
  @@ -425,6 +401,20 @@
           <map:parameter name="view-source" value="docs/samples/session/{1}.xsp"/>
        </map:transform>
        <map:serialize/>
  +   </map:match>
  +
  +   <!-- ========================== Stream ================================= -->
  +   <map:match pattern="request1">
  +     <map:generate type="stream">
  +       <map:parameter name="form-name" value="Foo"/>
  +     </map:generate>
  +     <map:serialize type="xml"/>
  +   </map:match>
  +
  +   <map:match pattern="Order">
  +     <map:generate src="docs/samples/stream/OrderPage.xml"/>
  +     <map:transform src="stylesheets/dynamic-page2html.xsl"/>
  +     <map:serialize type="html"/>
      </map:match>
   
      <!-- ========================== XSP Sources ============================== -->
  
  
  
  1.1                  
xml-cocoon2/src/org/apache/cocoon/generation/StreamGenerator.java
  
  Index: StreamGenerator.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.generation;
  
  import java.io.StringReader;
  import java.io.IOException;
  import java.util.Map;
  
  import org.apache.cocoon.Constants;
  import org.apache.cocoon.ProcessingException;
  import org.apache.cocoon.ResourceNotFoundException;
  import org.apache.cocoon.Roles;
  import org.apache.cocoon.components.parser.Parser;
  import org.apache.cocoon.environment.http.HttpRequest;
  import org.apache.cocoon.environment.Source;
  import org.apache.cocoon.environment.SourceResolver;
  import org.apache.cocoon.util.PostInputStream;
  
  import org.apache.avalon.excalibur.pool.Poolable;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.parameters.Parameters;
  
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXException;
  
  /**
   *
   * The <code>StreamGenerator</code> is a class that reads XML from a request 
InputStream
   * and generates SAX Events.
   * For the POST requests with mimetype of application/x-www-form-urlencoded the xml 
data is
   * expected to be associated with the name specified in the sitemap parameter.
   * For the POST requests with mimetypes: text/plain, text/xml, application/xml the 
xml data is in the body of teh POST
   * request and its length is specified by the value returned by getContentLength() 
method.
   * The StreamGenerator uses helper org.apache.cocoon.util.PostInputStream class for 
InputStream reading operations.
   * At the time that Parser is reading the data out of InputStream - Parser has no 
knowledge about the length of data to be read.
   * The only way to signal to the Parser that all data was read from the InputStream 
is to control reading operation - PostInputStream- and to return to
   * the requestor -1 when the number of bytes read is equal to the getContentLength() 
value.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]";>Kinga Dziembowski</a>
   * @version $Revision: 1.1 $ $Date: 2001/06/12 14:51:17 $
   */
  public class StreamGenerator extends ComposerGenerator {
  
      public static final String CLASS = StreamGenerator.class.getName();
  
      /** The parameter holding the name associated with the xml data  **/
      public static final String FORM_NAME = "form-name";
      /** The input source */
      private InputSource inputSource;
      /** The system ID of the input source */
      private String      systemID;
  
      /**
       * Set the current <code>ComponentManager</code> instance used by this
       * <code>Composable</code>.
       */
      public void compose(ComponentManager manager) {
          super.compose(manager);
      }
  
      /**
      * Recycle this component.
      * All instance variables are set to <code>null</code>.
      */
      public void recycle() {
          super.recycle();
          this.inputSource = null;
          this.systemID = null;
      }
  
      /**
      * Setup the stream generator.
      */
      public void setup(SourceResolver resolver, Map objectModel, String src, 
Parameters par)
          throws ProcessingException, SAXException, IOException {
          super.setup(resolver, objectModel, src, par);
      }
  
      /**
      * Generate XML data out of request InputStream.
      */
      public void generate()  throws IOException, SAXException, ProcessingException {
          Parser parser = null;
          String parameter = parameters.getParameter(StreamGenerator.FORM_NAME, null);
          int len = 0;
          try {
              HttpRequest request = (HttpRequest) 
objectModel.get(Constants.REQUEST_OBJECT);
              if 
(request.getContentType().equals("application/x-www-form-urlencoded")) {
                  String sXml = request.getParameter(parameter);
                  inputSource = new InputSource(new StringReader(sXml));
              } else if (request.getContentType().equals("text/plain")
                      || request.getContentType().equals("text/xml")
                      || request.getContentType().equals("application/xml")) {
                      len = request.getContentLength();
                      if (len > 0) {
                          PostInputStream anStream = new 
PostInputStream(request.getInputStream(), len);
                          inputSource = new InputSource(anStream);
                      } else {
                          throw new IOException("getContentLen() == 0");
                      }
                  } else {
                      throw new IOException("Unexpected getContentType(): " + 
request.getContentType());
                  }
              }
              getLogger().debug("processing stream ContentType= " + 
request.getContentType() + "ContentLen= " + len);
  
              parser = (Parser)this.manager.lookup(Roles.PARSER);
              parser.setContentHandler(super.contentHandler);
              parser.setLexicalHandler(super.lexicalHandler);
              parser.parse(this.inputSource);
          } catch (IOException e) {
              getLogger().error("StreamGenerator.generate()", e);
              throw new ResourceNotFoundException("StreamGenerator could not find 
resource", e);
          } catch (SAXException e) {
              getLogger().error("StreamGenerator.generate()", e);
              throw(e);
          } catch (Exception e){
              getLogger().error("Could not get parser", e);
              throw new ProcessingException("Exception in 
StreamGenerator.generate()",e);
          } finally {
              if (parser != null) this.manager.release((Component) parser);
          }
      }
  }
  
  
  
  1.1                  xml-cocoon2/src/org/apache/cocoon/util/PostInputStream.java
  
  Index: PostInputStream.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.util;
  
  import java.io.InputStream;
  import java.io.IOException;
  /**
   * The class PostInputStream is a wrapper for InputStream associated with POST 
message.
   * It allows to control read operation, restricting the number of bytes read to the 
value returned by getContentLen() method.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]";>Kinga Dziembowski</a>
   * @version $Id: PostInputStream.java,v 1.1 2001/06/12 14:51:17 giacomo Exp $
   */
  
  public class PostInputStream extends InputStream {
  
      /**
      * Class name
      */
      public static final String CLASS = PostInputStream.class.getName();
  
      /** The real InputStream object */
      private InputStream m_inputStream = null;
  
      /** The length of InputStream */
      private int m_contentLen = 0;
  
      /** The number of bytes read */
      protected int m_bytesRead = 0;
  
  
      /**
      * Creates a PostInputStream
      */
      public PostInputStream() {
          super();
      }
      /**
      * Creates a <code>PostInputStream</code> based on a real InputStream object with 
the specified
      * post message body length. Saves its  argument, the input stream
      * <code>m_inputStream</code>, for later use.
      *
      * @param   input     the underlying input stream.
      * @param   len   the post message body length.
      * @exception IllegalArgumentException  len <= 0.
      */
  
      public PostInputStream(final InputStream input, final int len) throws 
IllegalArgumentException {
          super();
          init(input, len );
      }
      /**
      * Sets the underlying input stream and contentLen value .
      *
      * @param inputStream the input stream; can not be null.
      * @param len the post message body length.
      *
      * @throws IllegalArgumentException
      */
      protected void init(final InputStream input, final int len) throws 
IllegalArgumentException {
          if (len <= 0) {
              throw new IllegalArgumentException("contentLen <= 0 ");
          }
          this.m_inputStream = input;
          this.m_contentLen = len;
      }
  
      /**
      * Sets the underlying input stream and contentLen value .
      *
      * @param inputStream the input stream; can not be null.
      * @param len the post message body length.
      *
      * @throws IOException
      */
      public synchronized void setInputStream(final InputStream input, final int len) 
throws IOException {
          if (m_inputStream != null) {
              close();
          }
          init(input, len);
      }
      /**
      * Returns the underlying input stream.
      *
      * @return inputStream the underlying InputStream.
      */
      public InputStream getInputStream() {
          return( m_inputStream );
      }
  
      /**
      * Returns the post message body length.
      *
      * @return m_contentLen;
      */
  
      public int getContentLen() {
          return( m_contentLen );
      }
  
      /**
      * Reads the next byte from the input stream.  If the end of the stream has been 
reached, this method returns -1.
      *
      * @return the next byte or -1 if at the end of the stream.
      *
      * @throws IOException
      */
      public synchronized int read() throws IOException {
  
          checkOpen();
          if (m_bytesRead == m_contentLen) {
              return -1;
          }
          int byt =  m_inputStream.read();
          if (byt != -1) {
             m_bytesRead++;
          }
          return byt;
      }
  
      /**
      * Reads bytes from this byte-input stream into the specified byte array,
      * starting at the given offset.
      *
      * <p> This method implements the general contract of the corresponding
      * <code>{@link InputStream#read(byte[], int, int) read}</code> method of
      * the <code>{@link InputStream}</code> class.
      * This method delegetes tre read operation to the underlying InputStream 
implementation class but it
      * controlls the number of bytes read from the stream.In the remote situation the 
underlying InputStream has no knowledge of
      * the length of the stream and the notion of the "end" is undefined. This 
wrapper class has a knowledge of the
      * length of data send by the requestor by the means of contentLength. This 
method returns the number of bytes read and
      * accumulates the total number of bytes read in m_bytesRead. When the 
m_bytesRead is equal to the specified contentLength
      * value the method returns returns -1 to signal the end of data.
      *
      * @param buffer the byte array to read into; can not be null.
      * @param offset the starting offset in the byte array.
      * @param len the maximum number of bytes to read.
      *
      * @return     the number of bytes read, or <code>-1</code> if the end of
      *             the stream has been reached.
      * @exception  IOException  if an I/O error occurs.
      */
  
      public synchronized int read(byte[] buffer, int offset, int len) throws 
IOException {
          checkOpen();
          if (m_bytesRead == m_contentLen) {
              return -1;
          }
          int num = m_inputStream.read(buffer, offset, len);
          if (num > 0) {
              m_bytesRead += num;
          }
          return num;
      }
  
      public synchronized int read(byte[] buffer) throws IOException {
  
          return read( buffer, 0, buffer.length);
      }
  
  
      /**
      * Checks to see if this stream is closed; if it is, an IOException is thrown.
      *
      * @throws IOException
      */
      protected void checkOpen() throws IOException {
          if (m_inputStream == null) {
              throw new IOException("InputStream closed");
          }
      }
  
      /**
           * See the general contract of the <code>skip</code>
       * method of <code>InputStream</code>.
       * Delegates execution to the underlying InputStream implementation class.
       * Checks to see if this stream is closed; if it is, an IOException is thrown.
       * @param      n   the number of bytes to be skipped.
       * @return     the actual number of bytes skipped.
       * @exception  IOException  if an I/O error occurs.
       */
      public synchronized long skip(long n) throws IOException {
          checkOpen();
          return m_inputStream.skip(n);
      }
  
      /**
      * Returns the number of bytes available from this input stream that can be read 
without the stream blocking.
      * Delegates execution to the underlying InputStream implementation class.
      * @return available the number of available bytes.
      *
      * @throws IOException
      */
      public synchronized int available() throws IOException {
          checkOpen();
          return m_inputStream.available();
      }
  
      /**
      * Tests if this input stream supports the <code>mark</code>
      * and <code>reset</code> methods. The <code>markSupported</code>
      * method of <code>BufferedInputStream</code> returns
      * <code>false</code>.
      *
      * @return  a <code>boolean</code> indicating if this stream type supports
      *          the <code>mark</code> and <code>reset</code> methods.
      * @see     java.io.InputStream#mark(int)
      * @see     java.io.InputStream#reset()
      */
      public boolean markSupported() {
          return false;
      }
  
      /**
      * Closes this input stream by closing the underlying stream and marking this one 
as closed.
      *
      * @throws IOException
      */
      public synchronized void close() throws IOException {
          if (m_inputStream == null) {
              return;
          }
          m_inputStream.close();
          m_inputStream = null;
          m_contentLen = 0;
          m_bytesRead = 0;
      }
  
      /**
      * Returns a String representation of this.
      *
      * @return string the String representation of this.
      */
      public String toString() {
          return getClass().getName() + "[inputStream=" + m_inputStream + ",  
contentLen=" + m_contentLen + "bytesRead=" + m_bytesRead + "]";
      }
  }
  
  
  1.1                  xml-cocoon2/webapp/docs/samples/stream/OrderPage.xml
  
  Index: OrderPage.xml
  ===================================================================
  <page>
   <title>B2B processing</title>
    <content>
     <FORM action="http://localhost:8080/cocoon/request1"; id="FORM1" method="post" 
name="FORM1">
        <para>Input your XML documet here:</para>
        <TEXTAREA cols="80" id="TEXTAREA1" name="Foo" rows="60">&lt;?xml 
version="1.0"?&gt;
                &lt;Orders&gt;
                &lt;OrderID&gt;20259&lt;/OrderID&gt;
                &lt;CustomerID&gt;WWWWWWW&lt;/CustomerID&gt;
                &lt;EmployeeID&gt;6&lt;/EmployeeID&gt;
                &lt;OrderDate&gt;2001-05-05 00:00:00&lt;/OrderDate&gt;
                &lt;RequiredDate&gt;2001-06-05 00:00:00&lt;/RequiredDate&gt;
                &lt;ShippedDate&gt;2001-06-01 00:00:00&lt;/ShippedDate&gt;
                &lt;ShipVia&gt;1&lt;/ShipVia&gt;
                &lt;Freight&gt;11.6100&lt;/Freight&gt;
                &lt;ShipName&gt;Thoms White&lt;/ShipName&gt;
                &lt;ShipAddress&gt;Somestr. 48&lt;/ShipAddress&gt;
                &lt;ShipCity&gt;Munster&lt;/ShipCity&gt;
                &lt;ShipRegion&gt;West&lt;/ShipRegion&gt;
                &lt;ShipPostalCode&gt;00000&lt;/ShipPostalCode&gt;
                &lt;ShipCountry&gt;Germany&lt;/ShipCountry&gt;
                &lt;OrderDetails&gt;
                        &lt;OrderID&gt;20259&lt;/OrderID&gt;
                        &lt;ProductID&gt;51&lt;/ProductID&gt;
                        &lt;UnitPrice&gt;42.4000&lt;/UnitPrice&gt;
                        &lt;Quantity&gt;40&lt;/Quantity&gt;
                        &lt;Discount&gt;0.0&lt;/Discount&gt;
                &lt;/OrderDetails&gt;
                &lt;OrderDetails&gt;
                                &lt;OrderID&gt;20259&lt;/OrderID&gt;
                                &lt;ProductID&gt;14&lt;/ProductID&gt;
                                &lt;UnitPrice&gt;18.6000&lt;/UnitPrice&gt;
                                &lt;Quantity&gt;9&lt;/Quantity&gt;
                                &lt;Discount&gt;0.0&lt;/Discount&gt;
                &lt;/OrderDetails&gt;
                &lt;OrderDetails&gt;
                                &lt;OrderID&gt;20259&lt;/OrderID&gt;
                                &lt;ProductID&gt;7&lt;/ProductID&gt;
                                &lt;UnitPrice&gt;12.4000&lt;/UnitPrice&gt;
                                &lt;Quantity&gt;30&lt;/Quantity&gt;
                                &lt;Discount&gt;0.0&lt;/Discount&gt;
                &lt;/OrderDetails&gt;
                &lt;Customers&gt;
                        &lt;CustomerID&gt;WWWWWWW&lt;/CustomerID&gt;
                        &lt;CompanyName&gt;Thomas White&lt;/CompanyName&gt;
                        &lt;ContactName&gt;Karin Black&lt;/ContactName&gt;
                        &lt;ContactTitle&gt;Marketing Manager&lt;/ContactTitle&gt;
                        &lt;Address&gt;Somestr. 48&lt;/Address&gt;
                        &lt;City&gt;Munster&lt;/City&gt;
                        &lt;Region&gt;West&lt;/Region&gt;
                        &lt;PostalCode&gt;00000&lt;/PostalCode&gt;
                        &lt;Country&gt;Germany&lt;/Country&gt;
                        &lt;Phone&gt;xxxx-yyyyyy&lt;/Phone&gt;
                        &lt;Fax&gt;xxxx-yyyyyy&lt;/Fax&gt;
                &lt;/Customers&gt;
        &lt;/Orders&gt;
        </TEXTAREA>
        
        <INPUT id="submit1" name="submit1" type="submit" value="Submit"/>
        </FORM>
        </content>
        </page>
  
  
  
  1.1                  xml-cocoon2/webapp/docs/samples/stream/ReadMeAdd.txt
  
  Index: ReadMeAdd.txt
  ===================================================================
                   Hewlett-Packard Bluestone Cocoon Project
  
                                 @version@
  
  
    What is being added?
    --------------------
  
  ·     StreamGenerator
  ·     PostInputStream
     
  StreamGenerator
  The StreamGenerator is a class that reads XML from an HttpRequest InputStream and 
generates SAX Events. StreamGenerator expects XML data coming as POST message. 
  
  For POST requests with mimetype of application/x-www-form-urlencoded, the xml data 
expects to be associated with the name specified in the sitemap parameter.
  
  For POST requests with mimetypes: text/plain, text/xml, application/xml the xml data 
is in the body of the POST request and its length is specified by the value returned 
by getContentLength() method.
    
  PostInputStream
  The StreamGenerator uses helper class org.apache.cocoon.util.PostInputStream for 
InputStream reading operations. At the time that Parser reads the data out of 
InputStream - Parser has no knowledge about the length of data to be    read. The only 
way to signal to the Parser that all data was read from the InputStream is to control 
reading   operation - PostInputStream- and to return to the requestor -1 when the 
number of bytes read is equal to the getContentLength() value.
    
  
  Installation Instructions
  ----------------------------
  TO VIEW ABOVE COCOON ELEMENTS "IN ACTION":
    
  Unzip the attached file. The necessary elements will "fall into place" within the 
Cocoon code. Compile it using instruction in Cocoon install file. Since the generator 
is a generic object, i.e. it can process any stream out of the POST message there are 
two ways to see StreamGenerator in action:
    1. To invoke URL http://localhost:8080/cocoon/Order
    2. To use telnet program to generate POST request
     
  The first option is not a "pure" stream invocation, but it is quick way to observe 
desired effects. The result of this invocation is a form containing the XML document 
embedded in the textarea of the form. Submission of this form will invoke 
StreamGenerator. The testarea name/value par is specified as a parameter in the 
sitemap definition for the StreamGenerator. The expected result is the submitted xml 
document send back to the browser.
    
  The second or "pure" option of testing StreamGenerator "in action," requires the use 
of Telnet program or any other process able to generate correct POST message. The 
procedure is:
  ·     To invoke telnet, connect to localhost 8080 and to use content of telnet.txt 
file as a post message. 
  ·     Here, the Copy-Paste method should be used.
  ·     Remember to hit the enter button twice enter after the contents of the post 
are set in telnet.
  
  It is important because Content-len is calculated assuming two "enter" in the end of 
http message. Once again, the performed task results in the mirror of the original 
document being sent back to the requestor. 
  
  The "pure" stream generation can be observed using the telnet utility where you can 
invoke a message targeting my processing. Any other method is good (URL object 
connection) as long the message is well formed.
  
  
  Compatibility Issues
  ------------------------
  
  The attached code was tested and is compatible with the Cocoon codebase ver.2.0a7 
taken from CVS Repository 5/26/2001. 
  The code was tested  on  Windows 2000 and  NT 4.0  using Tomcatt 4.0-b5 and newest 
Bluestone HP  application server. 
  
  
  
  1.1                  xml-cocoon2/webapp/docs/samples/stream/telnet.txt
  
  Index: telnet.txt
  ===================================================================
  POST /cocoon/request1 HTTP/1.1
  Content-Type: text/plain
  Content-Length:1513
  
  <?xml version="1.0"?>
  <Orders>
        <OrderID>20259</OrderID>
        <CustomerID>WWWWWWW</CustomerID>
        <EmployeeID>6</EmployeeID>
        <OrderDate>2001-05-05 00:00:00</OrderDate>
        <RequiredDate>2001-06-05 00:00:00</RequiredDate>
        <ShippedDate>2001-06-01 00:00:00</ShippedDate>
        <ShipVia>1</ShipVia>
        <Freight>11.6100</Freight>
        <ShipName>Thoms White</ShipName>
        <ShipAddress>Somestr. 48</ShipAddress>
        <ShipCity>Munster</ShipCity>
        <ShipRegion>West</ShipRegion>
        <ShipPostalCode>00000</ShipPostalCode>
        <ShipCountry>Germany</ShipCountry>
        <OrderDetails>
                <OrderID>20259</OrderID>
                <ProductID>51</ProductID>
                <UnitPrice>42.4000</UnitPrice>
                <Quantity>40</Quantity>
                <Discount>0.0</Discount>
        </OrderDetails>
        <OrderDetails>
                <OrderID>20259</OrderID>
                <ProductID>14</ProductID>
                <UnitPrice>18.6000</UnitPrice>
                <Quantity>9</Quantity>
                <Discount>0.0</Discount>
        </OrderDetails>
        <OrderDetails>
                <OrderID>20259</OrderID>
                <ProductID>7</ProductID>
                <UnitPrice>12.4000</UnitPrice>
                <Quantity>30</Quantity>
                <Discount>0.0</Discount>
        </OrderDetails>
        <Customers>
                <CustomerID>WWWWWWW</CustomerID>
                <CompanyName>Thomas White</CompanyName>
                <ContactName>Karin Black</ContactName>
                <ContactTitle>Marketing Manager</ContactTitle>
                <Address>Somestr. 48</Address>
                <City>Munster</City>
                <Region>West</Region>
                <PostalCode>00000</PostalCode>
                <Country>Germany</Country>
                <Phone>xxxx-yyyyyy</Phone>
                <Fax>xxxx-yyyyyy</Fax>
        </Customers>
  </Orders>
  
  

----------------------------------------------------------------------
In case of troubles, e-mail:     [EMAIL PROTECTED]
To unsubscribe, e-mail:          [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to