You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ni...@apache.org on 2013/06/19 01:35:12 UTC

svn commit: r1494376 - in /poi/trunk/src/ooxml: java/org/apache/poi/xwpf/extractor/ java/org/apache/poi/xwpf/usermodel/ testcases/org/apache/poi/xwpf/extractor/ testcases/org/apache/poi/xwpf/usermodel/

Author: nick
Date: Tue Jun 18 23:35:11 2013
New Revision: 1494376

URL: http://svn.apache.org/r1494376
Log:
Patch from Tim Allison from bug #54849 - Controlled content/Form (Std/StdBlock) content

Added:
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/IRunBody.java   (with props)
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/IRunElement.java   (with props)
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/ISDTContents.java   (with props)
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDT.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java
Modified:
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/extractor/XWPFWordExtractor.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/BodyElementType.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/BodyType.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFooter.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeader.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHyperlinkRun.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFPicture.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java
    poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/extractor/XWPFWordExtractor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/extractor/XWPFWordExtractor.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/extractor/XWPFWordExtractor.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/extractor/XWPFWordExtractor.java Tue Jun 18 23:35:11 2013
@@ -18,6 +18,7 @@ package org.apache.poi.xwpf.extractor;
 
 import java.io.IOException;
 import java.util.Iterator;
+import java.util.List;
 
 import org.apache.poi.POIXMLDocument;
 import org.apache.poi.POIXMLException;
@@ -26,13 +27,18 @@ import org.apache.poi.openxml4j.exceptio
 import org.apache.poi.openxml4j.opc.OPCPackage;
 import org.apache.poi.xwpf.model.XWPFCommentsDecorator;
 import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
+import org.apache.poi.xwpf.usermodel.IBodyElement;
+import org.apache.poi.xwpf.usermodel.IRunElement;
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
 import org.apache.poi.xwpf.usermodel.XWPFHyperlink;
 import org.apache.poi.xwpf.usermodel.XWPFHyperlinkRun;
 import org.apache.poi.xwpf.usermodel.XWPFParagraph;
 import org.apache.poi.xwpf.usermodel.XWPFRelation;
 import org.apache.poi.xwpf.usermodel.XWPFRun;
+import org.apache.poi.xwpf.usermodel.XWPFSDT;
 import org.apache.poi.xwpf.usermodel.XWPFTable;
+import org.apache.poi.xwpf.usermodel.XWPFTableCell;
+import org.apache.poi.xwpf.usermodel.XWPFTableRow;
 import org.apache.xmlbeans.XmlException;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
 
@@ -86,59 +92,11 @@ public class XWPFWordExtractor extends P
 		// Start out with all headers
 		extractHeaders(text, hfPolicy);
 		
-		// First up, all our paragraph based text
-		Iterator<XWPFParagraph> i = document.getParagraphsIterator();
-		while(i.hasNext()) {
-			XWPFParagraph paragraph = i.next();
-
-			try {
-				CTSectPr ctSectPr = null;
-				if (paragraph.getCTP().getPPr()!=null) {
-					ctSectPr = paragraph.getCTP().getPPr().getSectPr();
-				}
-
-				XWPFHeaderFooterPolicy headerFooterPolicy = null;
-
-				if (ctSectPr!=null) {
-					headerFooterPolicy = new XWPFHeaderFooterPolicy(document, ctSectPr);
-					extractHeaders(text, headerFooterPolicy);
-				}
-
-				// Do the paragraph text
-				for(XWPFRun run : paragraph.getRuns()) {
-				   text.append(run.toString());
-				   if(run instanceof XWPFHyperlinkRun && fetchHyperlinks) {
-				      XWPFHyperlink link = ((XWPFHyperlinkRun)run).getHyperlink(document);
-				      if(link != null)
-				         text.append(" <" + link.getURL() + ">");
-				   }
-				}
-
-				// Add comments
-				XWPFCommentsDecorator decorator = new XWPFCommentsDecorator(paragraph, null);
-				text.append(decorator.getCommentText()).append('\n');
-				
-				// Do endnotes and footnotes
-				String footnameText = paragraph.getFootnoteText();
-			   if(footnameText != null && footnameText.length() > 0) {
-			      text.append(footnameText + "\n");
-			   }
-
-				if (ctSectPr!=null) {
-					extractFooters(text, headerFooterPolicy);
-				}
-			} catch (IOException e) {
-				throw new POIXMLException(e);
-			} catch (XmlException e) {
-				throw new POIXMLException(e);
-			}
-		}
-
-		// Then our table based text
-		Iterator<XWPFTable> j = document.getTablesIterator();
-		while(j.hasNext()) {
-			text.append(j.next().getText()).append('\n');
-		}
+		// body elements
+      for (IBodyElement e : document.getBodyElements()){
+         appendBodyElementText(text, e);
+         text.append('\n');
+     }
 		
 		// Finish up with all the footers
 		extractFooters(text, hfPolicy);
@@ -146,6 +104,79 @@ public class XWPFWordExtractor extends P
 		return text.toString();
 	}
 
+   public void appendBodyElementText(StringBuffer text, IBodyElement e){
+      if (e instanceof XWPFParagraph){
+          appendParagraphText(text, (XWPFParagraph)e);
+      } else if (e instanceof XWPFTable){
+          appendTableText(text, (XWPFTable)e);
+      } else if (e instanceof XWPFSDT){
+          text.append(((XWPFSDT)e).getContent().getText());
+      }
+   }
+   
+   public void appendParagraphText(StringBuffer text, XWPFParagraph paragraph){
+      try {
+          CTSectPr ctSectPr = null;
+          if (paragraph.getCTP().getPPr()!=null) {
+              ctSectPr = paragraph.getCTP().getPPr().getSectPr();
+          }
+
+          XWPFHeaderFooterPolicy headerFooterPolicy = null;
+
+          if (ctSectPr!=null) {
+              headerFooterPolicy = new XWPFHeaderFooterPolicy(document, ctSectPr);
+              extractHeaders(text, headerFooterPolicy);
+          }
+
+
+          for(IRunElement run : paragraph.getRuns()) {
+              text.append(run.toString());
+              if(run instanceof XWPFHyperlinkRun && fetchHyperlinks) {
+                  XWPFHyperlink link = ((XWPFHyperlinkRun)run).getHyperlink(document);
+                  if(link != null)
+                      text.append(" <" + link.getURL() + ">");
+              }
+          }
+
+          // Add comments
+          XWPFCommentsDecorator decorator = new XWPFCommentsDecorator(paragraph, null);
+          String commentText = decorator.getCommentText();
+          if (commentText.length() > 0){
+              text.append(commentText).append('\n');
+          }
+
+          // Do endnotes and footnotes
+          String footnameText = paragraph.getFootnoteText();
+          if(footnameText != null && footnameText.length() > 0) {
+              text.append(footnameText + '\n');
+          }
+
+          if (ctSectPr!=null) {
+              extractFooters(text, headerFooterPolicy);
+          }
+      } catch (IOException e) {
+          throw new POIXMLException(e);
+      } catch (XmlException e) {
+          throw new POIXMLException(e);
+      }
+     
+   }
+
+   private void appendTableText(StringBuffer text, XWPFTable table){
+      //this works recursively to pull embedded tables from tables
+      for (XWPFTableRow row : table.getRows()){
+          List<XWPFTableCell> cells = row.getTableCells();
+          for (int i = 0; i < cells.size(); i++){
+              XWPFTableCell cell = cells.get(i);
+              text.append(cell.getTextRecursively());
+              if (i < cells.size()-1){
+                  text.append("\t");
+              }
+          }
+          text.append('\n');
+      }
+   }
+   
 	private void extractFooters(StringBuffer text, XWPFHeaderFooterPolicy hfPolicy) {
 		if(hfPolicy.getFirstPageFooter() != null) {
 			text.append( hfPolicy.getFirstPageFooter().getText() );

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/BodyElementType.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/BodyElementType.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/BodyElementType.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/BodyElementType.java Tue Jun 18 23:35:11 2013
@@ -28,7 +28,8 @@ package org.apache.poi.xwpf.usermodel;
  *
  */
 public enum BodyElementType {
-	PARAGRAPH,
+   CONTENTCONTROL,
+   PARAGRAPH,
 	TABLE,
 	
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/BodyType.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/BodyType.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/BodyType.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/BodyType.java Tue Jun 18 23:35:11 2013
@@ -21,6 +21,7 @@ package org.apache.poi.xwpf.usermodel;
  * The different kinds of {@link IBody} that exist
  */
 public enum BodyType {
+    CONTENTCONTROL,
     DOCUMENT,
     HEADER,
     FOOTER,

Added: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/IRunBody.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/IRunBody.java?rev=1494376&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/IRunBody.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/IRunBody.java Tue Jun 18 23:35:11 2013
@@ -0,0 +1,28 @@
+/* ====================================================================
+   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.poi.xwpf.usermodel;
+
+import org.apache.poi.POIXMLDocumentPart;
+
+/**
+ * Simple interface describing both {@link XWPFParagraph}
+ * and {@link XWPFSDT}
+ */
+public interface IRunBody {
+   public XWPFDocument getDocument();
+   public POIXMLDocumentPart getPart();
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/IRunBody.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/IRunElement.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/IRunElement.java?rev=1494376&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/IRunElement.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/IRunElement.java Tue Jun 18 23:35:11 2013
@@ -0,0 +1,26 @@
+/* ====================================================================
+   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.poi.xwpf.usermodel;
+
+/**
+ * Common interface for things that can occur
+ *  where a run (text with common stylings) can,
+ *  eg {@link XWPFRun} or {@link XWPFSDT}.
+ * More methods to follow shortly!
+ */
+public interface IRunElement {
+}
\ No newline at end of file

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/IRunElement.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/ISDTContents.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/ISDTContents.java?rev=1494376&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/ISDTContents.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/ISDTContents.java Tue Jun 18 23:35:11 2013
@@ -0,0 +1,25 @@
+/* ====================================================================
+   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.poi.xwpf.usermodel;
+
+/**
+ * Interface for anything that can be within a STD:
+ *  {@link XWPFRun}, {@link XWPFTable}, {@link XWPFParagraph},
+ *  {@link XWPFSDT} etc
+ */
+public interface ISDTContents {
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/ISDTContents.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java Tue Jun 18 23:35:11 2013
@@ -98,6 +98,7 @@ public class XWPFDocument extends POIXML
     protected List<XWPFHyperlink> hyperlinks = new ArrayList<XWPFHyperlink>();
     protected List<XWPFParagraph> paragraphs = new ArrayList<XWPFParagraph>();
     protected List<XWPFTable> tables = new ArrayList<XWPFTable>();
+    protected List<XWPFSDT> contentControls = new ArrayList<XWPFSDT>();
     protected List<IBodyElement> bodyElements = new ArrayList<IBodyElement>();
     protected List<XWPFPictureData> pictures = new ArrayList<XWPFPictureData>();
     protected Map<Long, List<XWPFPictureData>> packagePictures = new HashMap<Long, List<XWPFPictureData>>();
@@ -150,7 +151,11 @@ public class XWPFDocument extends POIXML
                     XWPFTable t = new XWPFTable((CTTbl) o, this);
                     bodyElements.add(t);
                     tables.add(t);
-                }
+                } else if (o instanceof CTSdtBlock){
+                   XWPFSDT c = new XWPFSDT((CTSdtBlock)o, this);
+                   bodyElements.add(c);
+                   contentControls.add(c);
+               }
             }
             cursor.dispose();
 
@@ -230,10 +235,10 @@ public class XWPFDocument extends POIXML
         for(POIXMLDocumentPart p : getRelations()){
             String relation = p.getPackageRelationship().getRelationshipType();
             if (relation.equals(XWPFRelation.FOOTNOTE.getRelation())) {
-                FootnotesDocument footnotesDocument = FootnotesDocument.Factory.parse(p.getPackagePart().getInputStream());
                 this.footnotes = (XWPFFootnotes)p;
                 this.footnotes.onDocumentRead();
-
+                // Warning - this apparently doubles footnotes - see bug #????
+                FootnotesDocument footnotesDocument = FootnotesDocument.Factory.parse(p.getPackagePart().getInputStream());
                 for(CTFtnEdn ctFtnEdn : footnotesDocument.getFootnotes().getFootnoteList()) {
                     footnotes.addFootnote(ctFtnEdn);
                 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFooter.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFooter.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFooter.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFooter.java Tue Jun 18 23:35:11 2013
@@ -34,6 +34,7 @@ import org.apache.xmlbeans.XmlOptions;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtr;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTNumbering;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.FtrDocument;
 
@@ -61,6 +62,7 @@ public class XWPFFooter extends XWPFHead
                 tables.add(t);
                 bodyElements.add(t);
             }
+            
         }
         cursor.dispose();
     }
@@ -118,6 +120,10 @@ public class XWPFFooter extends XWPFHead
                     tables.add(t);
                     bodyElements.add(t);
                 }
+                if (o instanceof CTSdtBlock){
+                   XWPFSDT c = new XWPFSDT((CTSdtBlock)o, this);
+                   bodyElements.add(c);
+               }
             }
             cursor.dispose();
         } catch (Exception e) {

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFootnote.java Tue Jun 18 23:35:11 2013
@@ -26,6 +26,7 @@ import org.apache.xmlbeans.XmlObject;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
 
@@ -37,20 +38,44 @@ public class XWPFFootnote implements Ite
 
     private CTFtnEdn ctFtnEdn;
     private XWPFFootnotes footnotes;
+    private XWPFDocument document;
 
     public XWPFFootnote(CTFtnEdn note, XWPFFootnotes xFootnotes) {
        footnotes = xFootnotes;
        ctFtnEdn = note;
-       for (CTP p : ctFtnEdn.getPList()) {
-          paragraphs.add(new XWPFParagraph(p, this));
-       }
+       document = xFootnotes.getXWPFDocument();
+       init();
     }
 
     public XWPFFootnote(XWPFDocument document, CTFtnEdn body) {
-        for (CTP p : body.getPList()) {
-            paragraphs.add(new XWPFParagraph(p, document));
-        }
-    }
+       ctFtnEdn = body;
+       this.document = document;
+       init();
+    }
+    
+    private void init(){
+       XmlCursor cursor = ctFtnEdn.newCursor();
+       //copied from XWPFDocument...should centralize this code
+       //to avoid duplication
+       cursor.selectPath("./*");
+       while (cursor.toNextSelection()) {
+           XmlObject o = cursor.getObject();
+           if (o instanceof CTP) {
+               XWPFParagraph p = new XWPFParagraph((CTP) o, this);
+               bodyElements.add(p);
+               paragraphs.add(p);
+           } else if (o instanceof CTTbl) {
+               XWPFTable t = new XWPFTable((CTTbl) o, this);
+               bodyElements.add(t);
+               tables.add(t);
+           } else if (o instanceof CTSdtBlock){
+               XWPFSDT c = new XWPFSDT((CTSdtBlock)o, this);
+               bodyElements.add(c);
+           }
+
+       }
+       cursor.dispose();
+   }
 
     public List<XWPFParagraph> getParagraphs() {
         return paragraphs;
@@ -314,7 +339,7 @@ public class XWPFFootnote implements Ite
      * @see org.apache.poi.xwpf.usermodel.IBody#getXWPFDocument()
      */
     public  XWPFDocument getXWPFDocument() {
-        return footnotes.getXWPFDocument();
+        return document;
     }
 
     /**

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeader.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeader.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeader.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeader.java Tue Jun 18 23:35:11 2013
@@ -35,6 +35,7 @@ import org.apache.xmlbeans.XmlOptions;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtr;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTNumbering;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.HdrDocument;
 
@@ -121,6 +122,10 @@ public class XWPFHeader extends XWPFHead
                     tables.add(t);
                     bodyElements.add(t);
                 }
+                if (o instanceof CTSdtBlock){
+                    XWPFSDT c = new XWPFSDT((CTSdtBlock)o, this);
+                    bodyElements.add(c);
+                }
             }
             cursor.dispose();
         } catch (XmlException e) {

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHeaderFooter.java Tue Jun 18 23:35:11 2013
@@ -129,7 +129,7 @@ public abstract class XWPFHeaderFooter e
      */
     public String getText() {
         StringBuffer t = new StringBuffer();
-
+      //TODO: simplify this to get ibody elements in order
         for(int i=0; i<paragraphs.size(); i++) {
             if(! paragraphs.get(i).isEmpty()) {
                 String text = paragraphs.get(i).getText();
@@ -148,7 +148,12 @@ public abstract class XWPFHeaderFooter e
                 t.append('\n');
             }
         }
-
+        
+        for (IBodyElement bodyElement : getBodyElements()){
+           if (bodyElement instanceof XWPFSDT){
+               t.append(((XWPFSDT) bodyElement).getContent().getText()+'\n');
+           }
+       }
         return t.toString(); 
     }
 

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHyperlinkRun.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHyperlinkRun.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHyperlinkRun.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFHyperlinkRun.java Tue Jun 18 23:35:11 2013
@@ -27,7 +27,7 @@ public class XWPFHyperlinkRun extends XW
 {
    private CTHyperlink hyperlink;
    
-   public XWPFHyperlinkRun(CTHyperlink hyperlink, CTR run, XWPFParagraph p) {
+   public XWPFHyperlinkRun(CTHyperlink hyperlink, CTR run, IRunBody p) {
       super(run, p);
       this.hyperlink = hyperlink;
    }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java Tue Jun 18 23:35:11 2013
@@ -40,6 +40,7 @@ import org.openxmlformats.schemas.wordpr
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRunTrackChange;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtContentRun;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtRun;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSimpleField;
@@ -61,12 +62,13 @@ import org.openxmlformats.schemas.wordpr
  *  actual text (possibly along with more styling) is held on
  *  the child {@link XWPFRun}s.</p>
  */
-public class XWPFParagraph implements IBodyElement {
+public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents {
     private final CTP paragraph;
     protected IBody part;
     /** For access to the document's hyperlink, comments, tables etc */
     protected XWPFDocument document;
     protected List<XWPFRun> runs;
+    protected List<IRunElement> iruns;
 
     private StringBuffer footnoteText = new StringBuffer();
 
@@ -82,6 +84,7 @@ public class XWPFParagraph implements IB
 
         // Build up the character runs
         runs = new ArrayList<XWPFRun>();
+        iruns = new ArrayList<IRunElement>();
         buildRunsInOrderFromXml(paragraph);
 
         // Look for bits associated with the runs
@@ -96,7 +99,7 @@ public class XWPFParagraph implements IB
                 XmlObject o = c.getObject();
                 if(o instanceof CTFtnEdnRef) {
                     CTFtnEdnRef ftn = (CTFtnEdnRef)o;
-                    footnoteText.append("[").append(ftn.getId()).append(": ");
+                    footnoteText.append(" [").append(ftn.getId()).append(": ");
                     XWPFFootnote footnote =
                         ftn.getDomNode().getLocalName().equals("footnoteReference") ?
                             document.getFootnoteByID(ftn.getId().intValue()) :
@@ -111,7 +114,7 @@ public class XWPFParagraph implements IB
                         footnoteText.append(p.getText());
                     }
 
-                    footnoteText.append("]");
+                    footnoteText.append("] ");
                 }
             }
             c.dispose();
@@ -129,30 +132,40 @@ public class XWPFParagraph implements IB
         while (c.toNextSelection()) {
             XmlObject o = c.getObject();
             if (o instanceof CTR) {
-                runs.add(new XWPFRun((CTR) o, this));
-            }
-            if (o instanceof CTHyperlink) {
-                CTHyperlink link = (CTHyperlink) o;
-                for (CTR r : link.getRList()) {
-                    runs.add(new XWPFHyperlinkRun(link, r, this));
-                }
-            }
-            if (o instanceof CTSdtRun) {
-                CTSdtContentRun run = ((CTSdtRun) o).getSdtContent();
-                for (CTR r : run.getRList()) {
-                    runs.add(new XWPFRun(r, this));
-                }
-            }
-            if (o instanceof CTRunTrackChange) {
-                for (CTR r : ((CTRunTrackChange) o).getRList()) {
-                    runs.add(new XWPFRun(r, this));
-                }
-            }
-            if (o instanceof CTSimpleField) {
-                for (CTR r : ((CTSimpleField) o).getRList()) {
-                    runs.add(new XWPFRun(r, this));
-                }
-            }
+               XWPFRun r = new XWPFRun((CTR) o, this);
+               runs.add(r);
+               iruns.add(r);
+           }
+           if (o instanceof CTHyperlink) {
+               CTHyperlink link = (CTHyperlink) o;
+               for (CTR r : link.getRList()) {
+                   XWPFHyperlinkRun hr = new XWPFHyperlinkRun(link, r, this);
+                   runs.add(hr);
+                   iruns.add(hr);
+               }
+           }
+           if (o instanceof CTSdtBlock) {
+               XWPFSDT cc = new XWPFSDT((CTSdtBlock) o, part);
+               iruns.add(cc);
+           }
+           if (o instanceof CTSdtRun) {
+               XWPFSDT cc = new XWPFSDT((CTSdtRun) o, part);
+               iruns.add(cc);
+           }
+           if (o instanceof CTRunTrackChange) {
+               for (CTR r : ((CTRunTrackChange) o).getRList()) {
+                   XWPFRun cr = new XWPFRun(r, this);
+                   runs.add(cr);
+                   iruns.add(cr);
+               }
+           }
+           if (o instanceof CTSimpleField) {
+               for (CTR r : ((CTSimpleField) o).getRList()) {
+                   XWPFRun cr = new XWPFRun(r, this);
+                   runs.add(cr);
+                   iruns.add(cr);
+               }
+           }
             if (o instanceof CTSmartTagRun) {
                 // Smart Tags can be nested many times. 
                 // This implementation does not preserve the tagging information
@@ -170,7 +183,15 @@ public class XWPFParagraph implements IB
     public List<XWPFRun> getRuns(){
         return Collections.unmodifiableList(runs);
     }
-
+    
+    /**
+     * Return literal runs and sdt/content control objects.
+     * @return List<IRunElement>
+     */
+    public List<IRunElement> getIRuns() {
+        return Collections.unmodifiableList(iruns);
+    }
+    
     public boolean isEmpty(){
         return !paragraph.getDomNode().hasChildNodes();
     }
@@ -181,12 +202,16 @@ public class XWPFParagraph implements IB
 
     /**
      * Return the textual content of the paragraph, including text from pictures
-     * in it.
+     * and sdt elements in it.
      */
     public String getText() {
         StringBuffer out = new StringBuffer();
-        for(XWPFRun run : runs) {
-            out.append(run.toString());
+        for (IRunElement run : iruns) {
+            if (run instanceof XWPFSDT){
+                out.append(((XWPFSDT)run).getContent().getText());
+            } else {
+                out.append(run.toString());
+            }
         }
         out.append(footnoteText);
         return out.toString();

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFPicture.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFPicture.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFPicture.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFPicture.java Tue Jun 18 23:35:11 2013
@@ -67,7 +67,7 @@ public class XWPFPicture {
         }
 
         String blipId = blipProps.getBlip().getEmbed();
-        POIXMLDocumentPart part = run.getParagraph().getPart();
+        POIXMLDocumentPart part = run.getParent().getPart();
         if (part != null)
         {
             POIXMLDocumentPart relatedPart = part.getRelationById(blipId);

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java Tue Jun 18 23:35:11 2013
@@ -74,24 +74,20 @@ import org.openxmlformats.schemas.drawin
 
 /**
  * XWPFRun object defines a region of text with a common set of properties
- *
- * @author Yegor Kozlov
- * @author Gregg Morris (gregg dot morris at gmail dot com) - added getColor(), setColor()
- *
  */
-public class XWPFRun {
+public class XWPFRun implements ISDTContents, IRunElement{
     private CTR run;
     private String pictureText;
-    private XWPFParagraph paragraph;
+    private IRunBody parent;
     private List<XWPFPicture> pictures;
 
     /**
      * @param r the CTR bean which holds the run attributes
      * @param p the parent paragraph
      */
-    public XWPFRun(CTR r, XWPFParagraph p) {
+    public XWPFRun(CTR r, IRunBody p) {
         this.run = r;
-        this.paragraph = p;
+        this.parent = p;
 
         /**
          * reserve already occupied drawing ids, so reserving new ids later will
@@ -143,6 +139,12 @@ public class XWPFRun {
             }
         }
     }
+    /**
+     * @deprecated Use {@link XWPFRun#XWPFRun(CTR, IRunBody)}
+     */
+    public XWPFRun(CTR r, XWPFParagraph p) {
+        this(r, (IRunBody)p);
+    }
 
     private List<CTPicture> getCTPictures(XmlObject o) {
         List<CTPicture> pictures = new ArrayList<CTPicture>(); 
@@ -173,11 +175,20 @@ public class XWPFRun {
     }
 
     /**
-     * Get the currenty referenced paragraph object
-     * @return current paragraph
+     * Get the currently referenced paragraph/SDT object
+     * @return current parent
+     */
+    public IRunBody getParent() {
+        return parent;
+    }
+    /**
+     * Get the currently referenced paragraph, or null if a SDT object
+     * @deprecated use {@link XWPFRun#getParent()} instead
      */
     public XWPFParagraph getParagraph() {
-        return paragraph;
+        if (parent instanceof XWPFParagraph)
+            return (XWPFParagraph)parent;
+        return null;
     }
 
     /**
@@ -185,8 +196,8 @@ public class XWPFRun {
      *         <code>null</code> if parent structure (paragraph > document) is not properly set.
      */
     public XWPFDocument getDocument() {
-        if (paragraph != null) {
-            return paragraph.getDocument();
+        if (parent != null) {
+            return parent.getDocument();
         }
         return null;
     }
@@ -663,7 +674,7 @@ public class XWPFRun {
      */
     public XWPFPicture addPicture(InputStream pictureData, int pictureType, String filename, int width, int height)
     throws InvalidFormatException, IOException {
-        XWPFDocument doc = paragraph.document;
+        XWPFDocument doc = parent.getDocument();
 
         // Add the picture + relationship
         String relationId = doc.addPictureData(pictureData, pictureType);
@@ -691,7 +702,7 @@ public class XWPFRun {
             inline.setDistL(0);
 
             CTNonVisualDrawingProps docPr = inline.addNewDocPr();
-            long id = getParagraph().document.getDrawingIdManager().reserveNew();
+            long id = getParent().getDocument().getDrawingIdManager().reserveNew();
             docPr.setId(id);
             /* This name is not visible in Word 2010 anywhere. */
             docPr.setName("Drawing " + id);

Added: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDT.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDT.java?rev=1494376&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDT.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDT.java Tue Jun 18 23:35:11 2013
@@ -0,0 +1,110 @@
+/* ====================================================================
+   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.poi.xwpf.usermodel;
+
+import java.util.List;
+
+import org.apache.poi.POIXMLDocumentPart;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtPr;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtRun;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTString;
+
+/**
+ * Experimental class to offer rudimentary read-only processing of 
+ *  of StructuredDocumentTags/ContentControl
+ *  
+ *
+ *
+ * WARNING - APIs expected to change rapidly
+ * 
+ */
+public class XWPFSDT implements IBodyElement, IRunBody, ISDTContents, IRunElement {
+   private final String title;
+   private final String tag;
+   private final XWPFSDTContent content;
+   private final IBody part;
+
+   public XWPFSDT(CTSdtRun sdtRun, IBody part){
+       this.part = part;
+       this.content = new XWPFSDTContent(sdtRun.getSdtContent(), part, this);
+       CTSdtPr pr = sdtRun.getSdtPr();
+       List<CTString> aliases = pr.getAliasList();
+       if (aliases != null && aliases.size() > 0){
+          title = aliases.get(0).getVal();
+       } else {
+          title = "";
+       }
+       @SuppressWarnings("deprecation")
+       CTString[] array = pr.getTagArray();
+       if (array != null && array.length > 0){
+          tag = array[0].getVal();
+       } else {
+          tag = "";
+       }
+  
+   }
+   public XWPFSDT(CTSdtBlock block, IBody part){
+      this.part = part;
+      this.content = new XWPFSDTContent( block.getSdtContent(), part, this);
+      CTSdtPr pr = block.getSdtPr();
+      List<CTString> aliases = pr.getAliasList();
+      if (aliases != null && aliases.size() > 0){
+         title = aliases.get(0).getVal();
+      } else {
+         title = "";
+      }
+      @SuppressWarnings("deprecation")
+      CTString[] array = pr.getTagArray();
+      if (array != null && array.length > 0){
+         tag = array[0].getVal();
+      } else {
+         tag = "";
+      }
+ 
+   }
+   public String getTitle(){
+      return title;
+   }
+   public String getTag(){
+      return tag;
+   }
+   public XWPFSDTContent getContent(){
+      return content;
+   }
+
+   public IBody getBody() {
+      // TODO Auto-generated method stub
+      return null;
+   }
+
+   public POIXMLDocumentPart getPart() {
+      return part.getPart();
+   }
+
+   public BodyType getPartType() {
+      return BodyType.CONTENTCONTROL;
+   }
+
+   public BodyElementType getElementType() {
+      return BodyElementType.CONTENTCONTROL;
+   }
+
+   public XWPFDocument getDocument() {
+      return part.getXWPFDocument();
+   }
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java?rev=1494376&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java Tue Jun 18 23:35:11 2013
@@ -0,0 +1,107 @@
+/* ====================================================================
+   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.poi.xwpf.usermodel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+import org.apache.xmlbeans.XmlCursor;
+import org.apache.xmlbeans.XmlObject;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
+
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtContentBlock;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtContentRun;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
+
+/**
+ * Experimental class to offer rudimentary read-only processing of 
+ *  of the contentblock of an SDT/ContentControl.
+ *  
+ *
+ *
+ * WARNING - APIs expected to change rapidly
+ * 
+ */
+public class XWPFSDTContent  {
+
+    // private final IBody part;
+    // private final XWPFDocument document;
+    private List<XWPFParagraph> paragraphs = new ArrayList<XWPFParagraph>();
+    private List<XWPFTable> tables = new ArrayList<XWPFTable>();
+    private List<XWPFRun> runs = new ArrayList<XWPFRun>();
+    private List<XWPFSDT> contentControls = new ArrayList<XWPFSDT>();
+    private List<ISDTContents> bodyElements = new ArrayList<ISDTContents>();
+
+    public XWPFSDTContent(CTSdtContentRun sdtRun, IBody part, IRunBody parent){
+        for (CTR ctr : sdtRun.getRList()){
+            XWPFRun run = new XWPFRun((CTR) ctr, parent);
+            runs.add(run);
+            bodyElements.add(run);
+        }
+    }
+    public XWPFSDTContent(CTSdtContentBlock block, IBody part, IRunBody parent){
+        XmlCursor cursor = block.newCursor();
+        cursor.selectPath("./*");
+        while (cursor.toNextSelection()) {
+            XmlObject o = cursor.getObject();
+            if (o instanceof CTP) {
+                XWPFParagraph p = new XWPFParagraph((CTP) o, part);
+                bodyElements.add(p);
+                paragraphs.add(p);
+            } else if (o instanceof CTTbl) {
+                XWPFTable t = new XWPFTable((CTTbl) o, part);
+                bodyElements.add(t);
+                tables.add(t);
+            } else if (o instanceof CTSdtBlock){
+                XWPFSDT c = new XWPFSDT(((CTSdtBlock)o), part);
+                bodyElements.add(c);
+                contentControls.add(c);
+            } else if (o instanceof CTR) {
+                XWPFRun run = new XWPFRun((CTR) o, parent);
+                runs.add(run);
+                bodyElements.add(run);
+            }
+        }
+    }
+
+    public String getText(){
+        StringBuilder text = new StringBuilder();
+        for (int i = 0; i < bodyElements.size(); i++){
+            Object o = bodyElements.get(i);
+            if (o instanceof XWPFParagraph){
+                text.append(((XWPFParagraph)o).getText());
+            } else if (o instanceof XWPFTable){
+                text.append(((XWPFTable)o).getText());
+            } else if (o instanceof XWPFSDT){
+                text.append(((XWPFSDT)o).getContent().getText());
+            } else if (o instanceof XWPFRun){
+                text.append(((XWPFRun)o).toString());
+            }
+            if (i < bodyElements.size()-1){
+                text.append("\n");
+            }
+        }
+        return text.toString();
+    }
+
+    public String toString(){
+        return getText();
+    }
+}

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java Tue Jun 18 23:35:11 2013
@@ -43,7 +43,7 @@ import org.openxmlformats.schemas.wordpr
  * <p>Specifies the contents of a table present in the document. A table is a set
  * of paragraphs (and other block-level content) arranged in rows and columns.</p>
  */
-public class XWPFTable implements IBodyElement {
+public class XWPFTable implements IBodyElement, ISDTContents {
     protected StringBuffer text = new StringBuffer();
     private CTTbl ctTbl;
     protected List<XWPFTableRow> tableRows;

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableCell.java Tue Jun 18 23:35:11 2013
@@ -28,6 +28,8 @@ import org.apache.xmlbeans.XmlCursor;
 import org.apache.xmlbeans.XmlObject;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtRun;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTShd;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
@@ -97,6 +99,15 @@ public class XWPFTableCell implements IB
                 tables.add(t);
                 bodyElements.add(t);
             }
+            if (o instanceof CTSdtBlock){
+               XWPFSDT c = new XWPFSDT((CTSdtBlock)o, this);
+               bodyElements.add(c);
+            }
+            if (o instanceof CTSdtRun){
+               XWPFSDT c = new XWPFSDT((CTSdtRun)o, this);
+               System.out.println(c.getContent().getText());
+               bodyElements.add(c);
+            }
         }
         cursor.dispose();
     }
@@ -407,6 +418,48 @@ public class XWPFTableCell implements IB
 	return text.toString();
     }
 
+    /**
+     * extracts all text recursively through embedded tables and embedded SDTs
+     */
+    public String getTextRecursively(){
+
+        StringBuffer text = new StringBuffer();
+        for (int i = 0; i < bodyElements.size(); i++){
+            boolean isLast = (i== bodyElements.size()-1)? true : false;
+            appendBodyElementText(text, bodyElements.get(i), isLast);
+        }
+
+        return text.toString();
+    }
+
+    private void appendBodyElementText(StringBuffer text, IBodyElement e, boolean isLast){
+        if (e instanceof XWPFParagraph){
+            text.append(((XWPFParagraph)e).getText());
+            if (isLast == false){
+                text.append('\t');
+            }
+        } else if (e instanceof XWPFTable){
+            XWPFTable eTable = (XWPFTable)e;
+            for (XWPFTableRow row : eTable.getRows()){
+                for (XWPFTableCell cell : row.getTableCells()){
+                    List<IBodyElement> localBodyElements = cell.getBodyElements();
+                    for (int i = 0; i < localBodyElements.size(); i++){
+                        boolean localIsLast = (i== localBodyElements.size()-1)? true : false;
+                        appendBodyElementText(text, localBodyElements.get(i), localIsLast);
+                    }
+                }
+            }
+
+            if (isLast == false){
+                text.append('\n');
+            }
+        } else if (e instanceof XWPFSDT){
+            text.append(((XWPFSDT)e).getContent().getText());
+            if (isLast == false){
+                text.append('\t');
+            }
+        }
+    }
 
     /**
      * get the TableCell which belongs to the TableCell

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTableRow.java Tue Jun 18 23:35:11 2013
@@ -132,6 +132,9 @@ public class XWPFTableRow {
 	    for (CTTc tableCell : ctRow.getTcList()) {
 		cells.add(new XWPFTableCell(tableCell, this, table.getBody()));
 	    }
+       //TODO: it is possible to have an SDT that contains a cell in within a row
+       //need to modify this code so that it pulls out SDT wrappers around cells, too.
+
 	    this.tableCells = cells;
 	}
 	return tableCells;

Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java?rev=1494376&r1=1494375&r2=1494376&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java (original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java Tue Jun 18 23:35:11 2013
@@ -80,10 +80,10 @@ public class TestXWPFWordExtractor exten
                 "  \n(V) ILLUSTRATIVE CASES\n\n"
         ));
         assertTrue(text.contains(
-                "As well as gaining " + euro + "90 from child benefit increases, he will also receive the early childhood supplement of " + euro + "250 per quarter for Vincent for the full four quarters of the year.\n\n\n\n \n\n\n"
+                "As well as gaining " + euro + "90 from child benefit increases, he will also receive the early childhood supplement of " + euro + "250 per quarter for Vincent for the full four quarters of the year.\n\n\n\n"// \n\n\n"
         ));
         assertTrue(text.endsWith(
-                "11.4%\t\t90\t\t\t\t\t250\t\t1,310\t\n\n"
+                "11.4%\t\t90\t\t\t\t\t250\t\t1,310\t\n\n \n\n\n"
         ));
 
         // Check number of paragraphs
@@ -317,4 +317,39 @@ public class TestXWPFWordExtractor exten
         
         extractor.close();
     }
+    
+    /**
+     * Test for basic extraction of SDT content
+     * @throws IOException
+     */
+    public void testSimpleControlContent() throws IOException {
+        XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Bug54849.docx");
+        String[] targs = new String[]{
+                "header_rich_text",
+                "rich_text",
+                "rich_text_pre_table\nrich_text_cell1\t\t\t\n\nrich_text_post_table",
+                "plain_text_no_newlines",
+                "plain_text_with_newlines1\nplain_text_with_newlines2\n",
+                "watermelon\n",
+                "dirt\n",
+                "4/16/2013\n",
+                "rich_text_in_paragraph_in_cell",
+                "footer_rich_text",
+                "footnote_sdt",
+                "endnote_sdt"
+        };
+        XWPFWordExtractor ex = new XWPFWordExtractor(doc);
+        String s = ex.getText().toLowerCase();
+        int hits = 0;
+    
+        for (String targ : targs){
+            boolean hit = false;
+            if (s.indexOf(targ) > -1){
+                hit = true;
+                hits++;
+            }
+            assertEquals("controlled content loading-"+targ, true, hit);
+        }
+        assertEquals("controlled content loading hit count", targs.length, hits);
+    }
 }

Added: poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java?rev=1494376&view=auto
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java (added)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java Tue Jun 18 23:35:11 2013
@@ -0,0 +1,158 @@
+/* ====================================================================
+   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.poi.xwpf.usermodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.xwpf.XWPFTestDataSamples;
+
+public final class TestXWPFSDT extends TestCase {
+
+    /**
+     * Test simple tag and title extraction from SDT
+     * @throws Exception
+     */
+    public void testTagTitle() throws Exception {
+        XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Bug54849.docx");
+        String tag = null;
+        String title= null;
+        List<XWPFSDT> sdts = extractAllSDTs(doc);
+        for (XWPFSDT sdt :sdts){
+            if (sdt.getContent().toString().equals("Rich_text")){
+                tag = "MyTag";
+                title = "MyTitle";
+                break;
+            }
+        }
+        // TODO Fix footnotes issues then enable
+//        assertEquals("controls size", 12, sdts.size());
+
+        assertEquals("tag", "MyTag", tag);
+        assertEquals("title", "MyTitle", title);
+    }
+
+
+    public void testGetSDTs() throws Exception{
+        String[] contents = new String[]{
+                "header_rich_text",
+                "Rich_text",
+                "Rich_text_pre_table\nRich_text_cell1\t\t\t\n\nRich_text_post_table",
+                "Plain_text_no_newlines",
+                "Plain_text_with_newlines1\nplain_text_with_newlines2",
+                "Watermelon",
+                "Dirt",
+                "4/16/2013",
+                "rich_text_in_paragraph_in_cell",
+                "Footer_rich_text",
+                "Footnote_sdt",
+                "Endnote_sdt"
+
+        };
+        XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Bug54849.docx");
+        List<XWPFSDT> sdts = extractAllSDTs(doc);
+
+        // TODO Fix footnotes issue
+/*
+        assertEquals("number of sdts", contents.length, sdts.size());
+
+        for (int i = 0; i < sdts.size(); i++){//contents.length; i++){
+            XWPFSDT sdt = sdts.get(i);
+
+            assertEquals(i+ ": " + contents[i], contents[i], sdt.getContent().toString());
+        } 
+*/
+    }
+
+    public void testFailureToGetSDTAsCell() throws Exception{
+        /**
+         * The current code fails to extract an sdt if it comprises/is the parent
+         * of a cell in a table.
+         */
+        XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Bug54849.docx");
+        List<XWPFSDT> sdts = extractAllSDTs(doc);
+        boolean found = false;
+        for (XWPFSDT sdt : sdts){
+            if (sdt.getContent().getText().toLowerCase().indexOf("rich_text_in_cell") > -1){
+                found = true;
+            }
+        }
+        assertEquals("SDT as cell known failure", false, found);
+    }
+
+    private List<XWPFSDT> extractAllSDTs(XWPFDocument doc){
+
+        List<XWPFSDT> sdts = new ArrayList<XWPFSDT>();
+
+        List<XWPFHeader> headers = doc.getHeaderList();
+        for (XWPFHeader header : headers){
+            sdts.addAll(extractSDTsFromBodyElements(header.getBodyElements()));
+        }
+        sdts.addAll(extractSDTsFromBodyElements(doc.getBodyElements()));
+
+        List<XWPFFooter> footers = doc.getFooterList();
+        for (XWPFFooter footer : footers){
+            sdts.addAll(extractSDTsFromBodyElements(footer.getBodyElements()));
+        }
+
+        for (XWPFFootnote footnote : doc.getFootnotes()){
+
+            sdts.addAll(extractSDTsFromBodyElements(footnote.getBodyElements()));
+        }
+        for (Map.Entry<Integer, XWPFFootnote> e : doc.endnotes.entrySet()){
+            sdts.addAll(extractSDTsFromBodyElements(e.getValue().getBodyElements()));
+        }
+        return sdts;
+    }
+
+    private List<XWPFSDT> extractSDTsFromBodyElements(List<IBodyElement> elements){
+        List<XWPFSDT> sdts = new ArrayList<XWPFSDT>();
+        for (IBodyElement e : elements){
+            if (e instanceof XWPFSDT){
+                XWPFSDT sdt = (XWPFSDT)e;
+                sdts.add(sdt);
+            } else if (e instanceof XWPFParagraph){
+
+                XWPFParagraph p = (XWPFParagraph)e;
+                for (IRunElement e2 : p.getIRuns()){
+                    if (e2 instanceof XWPFSDT){
+                        XWPFSDT sdt = (XWPFSDT)e2;
+                        sdts.add(sdt);
+                    }
+                }
+            } else if (e instanceof XWPFTable){
+                XWPFTable table = (XWPFTable)e;
+                sdts.addAll(extractSDTsFromTable(table));
+            }
+        }
+        return sdts;
+    }
+
+    private List<XWPFSDT> extractSDTsFromTable(XWPFTable table){
+        List<XWPFSDT> sdts = new ArrayList<XWPFSDT>();
+        for (XWPFTableRow r : table.getRows()){
+            for (XWPFTableCell c : r.getTableCells()){
+                sdts.addAll(extractSDTsFromBodyElements(c.getBodyElements()));
+            }
+        }
+        return sdts;
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org