View Javadoc
1   /*
2    * Copyright (c) 2002-2020, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.wordtemplate.service;
35  
36  import java.util.ArrayList;
37  import java.util.List;
38  import java.util.regex.Matcher;
39  import java.util.regex.Pattern;
40  import org.apache.poi.xwpf.usermodel.BodyElementType;
41  import org.apache.poi.xwpf.usermodel.IBody;
42  import org.apache.poi.xwpf.usermodel.IBodyElement;
43  import org.apache.poi.xwpf.usermodel.XWPFParagraph;
44  import org.apache.poi.xwpf.usermodel.XWPFRun;
45  import org.apache.poi.xwpf.usermodel.XWPFTable;
46  import org.apache.poi.xwpf.usermodel.XWPFTableCell;
47  import org.apache.poi.xwpf.usermodel.XWPFTableRow;
48  import fr.paris.lutece.plugins.wordtemplate.business.IWordTemplateElement;
49  import fr.paris.lutece.plugins.wordtemplate.business.WordTemplate;
50  import java.util.Map;
51  import org.apache.poi.xwpf.usermodel.XWPFDocument;
52  import org.apache.poi.xwpf.usermodel.XWPFFooter;
53  import org.apache.poi.xwpf.usermodel.XWPFHeader;
54  
55  /**
56   * Parser of word templates
57   */
58  public class WordTemplateParser
59  {
60      private static final String INSTRUCTION_PATTERN = "\\$\\{.*?\\}|</?#.*?>";
61  
62      /**
63       * Replace properties by their value
64       *
65       * @param document
66       * @param model
67       * @return 
68       * @throws Exception
69       */
70      public WordTemplate parse( XWPFDocument document, Map<String, Object> model ) throws Exception
71      {
72          WordTemplatete/business/WordTemplate.html#WordTemplate">WordTemplate wordTemplate = new WordTemplate( );
73          List<IWordTemplateElement> listTemplateElements = new ArrayList<>( );
74  
75          // Parse headers
76          for ( XWPFHeader header : document.getHeaderList( ) )
77          {
78              listTemplateElements.addAll( findInstr( header ) );
79          }
80          // Parse footers
81          for ( XWPFFooter footer : document.getFooterList( ) )
82          {
83              listTemplateElements.addAll( findInstr( footer ) );
84          }
85  
86          // Parse the document
87          listTemplateElements.addAll( findInstr( document ) );
88  
89          wordTemplate.setListInstructions( listTemplateElements );
90  
91          return wordTemplate;
92      }
93  
94      /**
95       *
96       * @param body
97       */
98      public static void visitBody( IBody body )
99      {
100         for ( IBodyElement bodyElement : body.getBodyElements( ) )
101         {
102             if ( bodyElement.getElementType( ).equals( BodyElementType.PARAGRAPH ) )
103             {
104                 for ( XWPFRun run : ( (XWPFParagraph) bodyElement ).getRuns( ) )
105                 {
106                     //action
107                 }
108             }
109             if ( bodyElement.getElementType( ).equals( BodyElementType.TABLE ) )
110             {
111                 for ( XWPFTableRow row : ( (XWPFTable) bodyElement ).getRows( ) )
112                 {
113                     for ( XWPFTableCell cell : row.getTableCells( ) )
114                     {
115                         visitBody( cell );
116                     }
117                 }
118             }
119         }
120     }
121 
122     /**
123      *
124      * @param body
125      * @return
126      */
127     private List<IWordTemplateElement> findInstr( IBody body )
128     {
129         List<IWordTemplateElement> listInstruction = new ArrayList<>( );
130 
131         for ( IBodyElement bodyElement : body.getBodyElements( ) )
132         {
133             if ( bodyElement.getElementType( ).equals( BodyElementType.PARAGRAPH ) )
134             {
135                 listInstruction.addAll( findInstr( (XWPFParagraph) bodyElement ) );
136             }
137 
138             if ( bodyElement.getElementType( ).equals( BodyElementType.TABLE ) )
139             {
140                 for ( XWPFTableRow row : ( (XWPFTable) bodyElement ).getRows( ) )
141                 {
142                     for ( XWPFTableCell cell : row.getTableCells( ) )
143                     {
144                         findInstr( cell );
145                     }
146                 }
147             }
148         }
149 
150         return listInstruction;
151     }
152 
153     /**
154      *
155      * @param paragraph
156      * @return
157      */
158     private List<IWordTemplateElement> findInstr( XWPFParagraph paragraph )
159     {
160         List<IWordTemplateElement> listInstruction = new ArrayList<>( );
161         String text = paragraph.getParagraphText( );
162         Pattern pattern = Pattern.compile( INSTRUCTION_PATTERN );
163         Matcher matcher = pattern.matcher( text );
164 
165         while ( matcher.find( ) )
166         {
167             int start = matcher.start( );
168             int end = matcher.end( ) - 1;
169             formatInstr( paragraph, start, end );
170             for ( XWPFRun run : paragraph.getRuns( ) )
171             {
172                 if ( run.toString( ).equals( matcher.group( ) ) )
173                 {
174                     InstructionService instructionService = InstructionService.init( );
175                     IWordTemplateElement element = instructionService.createInstruction( run.toString( ), run );
176                     listInstruction.add( element );
177                     break;
178                 }
179             }
180         }
181 
182         return listInstruction;
183     }
184 
185     /**
186      *
187      * @param paragraph
188      * @param start
189      * @param end
190      */
191     private void formatInstr( XWPFParagraph paragraph, int start, int end )
192     {
193         boolean startFound = false;
194         boolean endFound = false;
195 
196         while ( !endFound )
197         {
198             int pos = 0, numRun = -1, nextPos;
199 
200             for ( XWPFRun run : paragraph.getRuns( ) )
201             {
202                 numRun++;
203                 nextPos = pos + run.toString( ).length( );
204 
205                 if ( start < pos && end >= nextPos )
206                 {
207                     XWPFRun prevRun = paragraph.getRuns( ).get( numRun - 1 );
208                     prevRun.setText( prevRun.toString( ) + run.toString( ), 0 );
209                     paragraph.removeRun( numRun );
210                     break;
211                 }
212                 if ( start >= pos && start < nextPos && !startFound )
213                 {
214                     int startPosInRun = start - pos;
215                     startFound = true;
216                     if ( start > pos )
217                     {
218                         WordService.splitRun( run, startPosInRun );
219                         break;
220                     }
221                 }
222                 if ( end >= pos && end < nextPos )
223                 {
224                     int endPosInRun = end - pos;
225                     endFound = true;
226 
227                     WordService.splitRun( run, endPosInRun + 1 );
228 
229                     if ( start < pos )
230                     {
231                         XWPFRun prevRun = paragraph.getRuns( ).get( numRun - 1 );
232                         run = paragraph.getRuns( ).get( numRun );
233                         prevRun.setText( prevRun.toString( ) + run.toString( ), 0 );
234                         paragraph.removeRun( numRun );
235                     }
236                     break;
237                 }
238 
239                 pos = nextPos;
240             }
241         }
242     }
243 }