001    /*
002     * Copyright (c) 2009 The openGion Project.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013     * either express or implied. See the License for the specific language
014     * governing permissions and limitations under the License.
015     */
016    package org.opengion.fukurou.xml;
017    
018    import java.io.File;
019    import java.io.IOException;
020    import java.io.BufferedReader;
021    import java.util.Map;
022    import java.util.WeakHashMap;
023    
024    import org.opengion.fukurou.util.FileUtil;
025    import org.opengion.fukurou.util.Closer;
026    import org.opengion.fukurou.util.LogWriter;
027    
028    /**
029     * こã?クラスは、jspファイルのXSLT変換に特化したã?Readerオブジェクトを作æ?するクラスですã?
030     * jspファイル に記述されるã?jsp:directive.include を見つけて、そのファイル属æ?に
031     * 記述されてã�?‚‹ファイルをã?インクルードしますã?
032     * Tomcat の特性上ã?インクルード時のファイルはã€?¼?­‰ã?エスケープを処ç�?�—ておく
033     * å¿?¦�がありますã?
034     * <del>(つまりã?オリジナルは、エスケープå?ç�?�ªしで、インクルードされるファイルはã€?
035     * 属æ?部åˆ??み、エスケープå?ç�?�Œå¿?¦�ですã?)</del>
036     * エスケープã?前å?ç�??、jsp:root タグのあるなしで判定しますã?
037     * 現時点ではã€?&amp; , &lt; , &lt;= , &gt; , &gt;= を前処ç�?�—ますã?
038     *
039     * <del>タグのBODY部に記述されてã�?‚‹ ??などもã?パã?スの対象になりますがã€?
040     * 擬似çš?�«処ç�?�—てã�?�¾すã?
041     * それは、ã?&nbsp;』ã?< 』ã?<=』につã�?�¦、å?ç�?�—てã�?�¾すã?
042     * こã?形以外ã?処ç�??、今ã?入れてã�?�¾せんã€?/del>
043     *
044     * JSP では、og:head タグでã€?lt;html&gt; をå?力したり、htmlend.jsp インクルードで
045     * &lt;/body&gt;&lt;/html&gt; をå?力してã�?�¾すが、フレーãƒ?‚„、フォワードなど、整合æ?ã�?
046     * 取れなã�?‚±ースがありますã?で、XML処ç�?”¨としてã€?lt;html&gt; をå?力してã�?�¾せんã€?
047     * 変換結果をã?正式な HTML ファイルとして再利用されるå?合ã?、ご注意くã�?�•ã�??
048     *
049     * なおã?こã?クラスは、ã?ルチスレãƒ?ƒ‰対応されてã�?�¾せんã€?
050     *
051     * @og.rev 4.0.0.2 (2007/12/10) 新規追åŠ?
052     *
053     * @version  4.0
054     * @author   Kazuhiko Hasegawa
055     * @since    JDK5.0,
056     */
057    public class JspIncludeReader {
058            private static final String CR = System.getProperty("line.separator");
059    
060            // 5.6.7.1 (2013/08/09) includeしたファイルをキャãƒ?‚·ュしておきますã?
061            private static final Map<String,String> includeFiles = new WeakHashMap<String,String>();
062    
063            // 5.6.7.1 (2013/08/09) ãƒ?ƒ�ãƒ?‚°用にincludeしたファイルを保存しておきますã?
064            private StringBuilder incFiles = new StringBuilder();
065    
066            // タグの属æ?の値のみを抜きå?してã�?�¾すã?特にã€?>& を含ã‚??合ã?
067            // 5.2.1.0 (2010/10/01) 仮å»?­¢
068            //      private static final Pattern ptn = Pattern.compile( "=[ \t]*\"([^\"]*[<>&].[^\"]*)\"" );
069    
070            /**
071             *      JSP のインクルードをè€??した、Reader オブジェクトを返しますã?
072             *
073             * @param file File
074             * @param encode String
075             *
076             * @return Reader
077             */
078    //      public Reader getReader( final File file,final String encode ) {
079    //              return new StringReader( getString( file,encode,false ) );
080    //      }
081    
082            /**
083             * JSP のインクルードをè€??した、JSPファイルをã?String で返しますã?
084             * こã?メソãƒ?ƒ‰は、å?部で再帰定義されてã�?�¾すã?つまりã?jsp:directive.include
085             * æ–?­—å?が見つかったå?合ã?、その代わりに、ファイル名を取å?して、もã�?¸?º¦
086             * こã?メソãƒ?ƒ‰を呼び出しますã?インクルードファイルとの関連をチェãƒ?‚¯する為に
087             * ãƒ?ƒŸーのspanタグをå?れておきますã?
088             * &lt;span type="jsp:directive" include="ファイルå�?&gt;&lt;!-- --&gt;&lt;/span&gt;
089             * ただしã?ソースチェãƒ?‚¯時にã€?
090             * <del>Tomcat の特性上ã?インクルード時のファイルはã€?¼?­‰ã?エスケープを処ç�?�—ておく
091             * å¿?¦�がありますが、オリジナルは、エスケープå?ç�?�—ませんã€?
092             * そã?åˆ?‚Š替えを、第?“引数の isEscape で制御してã�?�¾すã?</del>
093             * Ver4 以降で、インクルードファイルに、XML宣è¨?�¨、jsp:root を付与するケースがありますã?
094             * 擬似çš?�«取り込ã‚?�¨きには、XML宣è¨??削除しますã?
095             *
096             * @og.rev 5.2.1.0 (2010/10/01) directive.include で、XMLタグとroot タグは取り込まなã�??
097             * @og.rev 5.2.1.0 (2010/10/01) エスケープå?ç�??引数をå»?­¢しますã?
098             * @og.rev 5.6.5.2 (2013/06/21) 小細工å†?®¹の変更。replaceAll にするのと、スペã?スまたã?タブを使用しますã?
099             * @og.rev 5.6.7.1 (2013/08/09) コメントã?処ç�??バグ修正。includeファイル名保存ã?
100             * @og.rev 5.6.7.1 (2013/08/09) includeファイルが存在しなã�??合ã?、gf共有から取得するã?
101             * @og.rev 5.6.7.2 (2013/08/16) includeファイルを取り込ã‚??合ã?代わりのspanタグをå?力しておきますã?
102             * @og.rev 5.6.7.4 (2013/08/30) includeファイルの先é?のpageEncodingæŒ?®šã?チェãƒ?‚¯用 span タグの出åŠ?
103             *
104             * @param       file    JSPファイル
105             * @param       encode  ファイルのエンコーãƒ?
106             *
107             * @return      インクルードをè€??した、JSPファイル
108             */
109    //      public String getString( final File file,final String encode,final boolean isEscape ) {
110            public String getString( final File file,final String encode ) {
111                    StringBuilder buf = new StringBuilder() ;
112                    BufferedReader reader = FileUtil.getBufferedReader( file,encode );
113    
114                    // ファイルがã?jsp 直下かどã�?�‹を判断しますã?
115                    String parentFile = file.getParent() ;
116                    boolean isUnder = parentFile.endsWith( "\\jsp" );
117    
118                    int  cmntIn    = -1;
119                    int  cmntOut   = -1;
120                    boolean isCmnt = false;
121                    boolean isEscape = true;        // エスケープするかどã�?�‹(true:する/false:しなã�?
122                    try {
123                            String line ;
124                            while((line = reader.readLine()) != null) {
125                                    // 5.2.1.0 (2010/10/01) directive.include で、XMLタグは取り込まなã�??
126                                    if( line.indexOf( "<?xml" ) >= 0 && line.indexOf( "?>" ) >= 0 ) { continue; }
127                                    // jsp:root があれã?、エスケープå?ç�?‚’行わなã�?
128                                    if( line.indexOf( "<jsp:root" ) >= 0 ) { isEscape = false; }
129    
130                                    // コメントã?削除
131                                    cmntIn  = line.indexOf( "<!--" );
132                                    cmntOut = line.indexOf( "-->" );
133                                    if( cmntIn >= 0 && cmntOut >= 0 ) {
134    //                                      line = line.substring( 0,cmntIn ) + line.substring( cmntOut );
135                                            line = line.substring( 0,cmntIn ) + line.substring( cmntOut+3 );        // 5.6.7.1 (2013/08/09) コメントã?処ç�??バグ修正
136                                    }
137                                    else if( cmntIn >= 0 && cmntOut < 0 ) {
138                                            line = line.substring( 0,cmntIn );
139                                            isCmnt = true;
140                                    }
141                                    else if( cmntIn < 0  && cmntOut >= 0 ) {
142    //                                      line = line.substring( cmntOut );
143                                            line = line.substring( cmntOut+3 );                     // 5.6.7.1 (2013/08/09) コメントã?処ç�??バグ修正
144                                            isCmnt = false;
145                                    }
146                                    else if( isCmnt && cmntIn < 0 && cmntOut < 0 ) { continue; }
147    
148                                    // 特殊å?ç�?¼šog:head で html タグをå?力してã�?‚‹ã€?
149            //                      if( line.indexOf( "<og:head" ) >= 0 ) {
150            //                              buf.append( "<html>" );
151            //                      }
152    
153                                    if( isEscape ) {
154                                            // 5.6.5.2 (2013/06/21) 小細工å†?®¹の変更。replaceAll にするのと、スペã?スまたã?タブを使用しますã?
155                                            // & , < , <= , > , >= を前処ç�?�—ますã?
156    //                                      line = line.replace( "&"  ,"&amp;" );           // ちã‚?�£と小細工
157    //                                      line = line.replace( " < "," &lt; " );           // ちã‚?�£と小細工
158    //                                      line = line.replace( " > "," &gt; " );           // ちã‚?�£と小細工
159    //                                      line = line.replace( " <="," &lt;=" );           // ちã‚?�£と小細工
160    //                                      line = line.replace( " >="," &gt;=" );           // ちã‚?�£と小細工
161                                            line = line.replaceAll( "&"  ,"&amp;" );                                // ちã‚?�£と小細工
162                                            line = line.replaceAll( "[ \\t]<[ \\t]"," &lt; " );              // ちã‚?�£と小細工
163                                            line = line.replaceAll( "[ \\t]>[ \\t]"," &gt; " );              // ちã‚?�£と小細工
164                                            line = line.replaceAll( "[ \\t]<="," &lt;=" );                   // ちã‚?�£と小細工
165                                            line = line.replaceAll( "[ \\t]>="," &gt;=" );                   // ちã‚?�£と小細工
166            // 5.2.1.0 (2010/10/01) 仮å»?­¢
167            //                              Matcher mtch = ptn.matcher( line );
168            //                              int adrs = 0;
169            //                              StringBuilder buf2 = new StringBuilder();
170            //                              while( mtch.find(adrs) ) {
171            //                                      String grp = mtch.group(1);
172            //                                      String htm = StringUtil.htmlFilter( grp );
173            //                                      int in = mtch.start(1);
174            //                                      buf2.append( line.substring( adrs,in ) ).append( htm );
175            //                                      adrs = mtch.end(1);
176            //                              }
177            //                              buf2.append( line.substring( adrs ) );
178            //                              line = buf2.toString();
179                                    }
180    
181                                    int st = line.indexOf( "<jsp:directive.include" );
182                                    if( st < 0 ) { buf.append( line ); } // include が無ければ、そのまま追åŠ?
183                                    else {
184                                            buf.append( line.substring( 0,st ) );
185                                            int fin = line.indexOf( '\"',st );              // ファイルのæœ??
186                                            int fout= line.indexOf( '\"',fin+1 );   // ファイルのæœ?¾?
187                                            String fname = line.substring( fin+1,fout );    // ファイルå�?
188    
189                                            // 5.6.7.2 (2013/08/16) includeファイルを取り込ã‚??合ã?代わりのspanタグをå?力しておきますã?
190                                            buf.append( "<span type=\"jsp:directive\"" )
191                                                    .append( " include=\"" ).append( fname ).append( "\" ><!-- --></span>" ) ;
192    
193                                            // htmlend.jsp の インクルードã?行わなã�??
194                                            if( fname.endsWith( "htmlend.jsp" ) ) {
195                                                    if( buf.indexOf( "<body" ) >= 0 && buf.indexOf( "</body>" ) < 0 ) {
196                                                            buf.append( "</body>" );
197                                                    }
198    
199            //                                      if( buf.indexOf( "<html" ) >= 0 ) {
200            //                                              buf.append( "</html>" );
201            //                                      }
202                                            }
203                                            else {
204                                                    // 5.6.7.1 (2013/08/09) ãƒ?ƒ�ãƒ?‚°用にincludeしたファイルを保存しておきますã?
205                                                    if( incFiles.length() > 0 ) { incFiles.append( " , " ); }
206                                                    incFiles.append( fname );
207    
208                                                    // 5.6.7.1 (2013/08/09) includeしたファイルをキャãƒ?‚·ュから検索しますã?
209                                                    String fileData = includeFiles.get( fname );    // キャãƒ?‚·ュを検索(fname がキー)
210                                                    if( fileData == null ) {
211                                                            // ちã‚?�£と小細工
212                                                            String fname2 = fname ;
213                                                            // include するファイルはã€?jsp/ からの絶対パスã€?
214                                                            // jsp 直下ã?場合ã?ã€?/ 、それ以外ã?ã€?./ と置き換えますã?
215                                                            if( isUnder ) { fname2 = fname2.replace( "/jsp/","./" ); }
216                                                            else              { fname2 = fname2.replace( "/jsp/","../" ); }
217                                                            // 5.6.7.1 (2013/08/09) includeファイルが存在しなã�??合ã?、gf共有から取得するã?
218                                                            File newfile = new File( parentFile,fname2 );
219                                                            if( !newfile.exists() ) {
220                                                                    if( fname2.contains( "/common/" ) || fname2.contains( "/menu/" ) ) {
221                                                                            // 本当ã? classPathから、取得すべきã?
222                                                                            // 今ã?、実行環å¢??相対パスの位置に、gf/jsp/common,menu のファイルがå¿?¦�ã?
223                                                                            fname2 = ( isUnder )
224                                                                                                    ?       "./../../gf/jsp/"  + fname2.substring( 2 )
225                                                                                                    :       "../../../gf/jsp/" + fname2.substring( 3 ) ;
226                                                                            newfile = new File( parentFile,fname2 );                // ここでなければ、エラーになるã?
227                                                                    }
228                                                            }
229                                                            fileData = getString( newfile,encode );
230                                                            // 5.6.7.4 (2013/08/30) includeファイルの先é?のpageEncodingæŒ?®šã?チェãƒ?‚¯用 span タグの出åŠ?
231                                                            // インクルードファイルの先é?には、pageEncoding="UTF-8" 宣è¨?�Œå¿?¦?UTF-8かどã�?�‹は未チェãƒ?‚¯)
232                                                            if( ! fileData.startsWith( "<jsp:directive.page pageEncoding" ) ) {
233                                                                    // チェãƒ?‚¯用のspanタグをå?力しておきますã?
234                                                                    buf.append( "<span type=\"jsp:directive\"" )
235                                                                            .append( " pageEncoding=\"non\" file=\"" ).append( fname ).append( "\" ><!-- --></span>" ) ;
236                                                            }
237    
238                                                            // 5.6.7.1 (2013/08/09) includeしたファイルをキャãƒ?‚·ュしておきますã?
239                                                            includeFiles.put( fname,fileData );                     // includeファイルをキャãƒ?‚·ュ(fname がキー)
240                                                    }
241    
242    //                                              buf.append( getString( newfile,encode ) );
243                                                    buf.append( fileData );
244                                            }
245                                            int tagout = line.indexOf( "/>",fout+1 );    // タグのæœ?¾?
246    
247                                            buf.append( line.substring( tagout+2 ) );
248                                    }
249    
250                                    // og:commonForward を見つけた場合ã?、最後に html タグをå?力するã?
251            //                      if( line.indexOf( "<og:commonForward" ) >= 0 ) {
252            //                              buf.append( "</html>" );
253            //                      }
254    
255                                    buf.append( CR );
256                            }
257                    }
258                    catch( IOException ex ) {
259                            LogWriter.log( ex );
260                    }
261                    finally {
262                            Closer.ioClose( reader );
263                    }
264                    return buf.toString();
265            }
266    
267            /**
268             * インクルードしたファイルå�?相対パス)のリスト文字å?を返しますã?
269             * 通常は、XSLT変換処ç�?�§エラーが発生したå?合ã?、includeファイルの整合æ?ã�?
270             * おかしい場合が多いので、デバッグæƒ??として利用しますã?
271             * ただしã?エラー発生時の位置特定まではできませんã€?
272             *
273             * こã?å†?ƒ¨変数は、インスタンス変数ですã?で、includeファイルのキャãƒ?‚·ュとは寿命が異なりますã?
274             *
275             * @og.rev 5.6.7.1 (2013/08/09) 新規追åŠ?
276             *
277             * @return includeファイル名ã?リスト文字å?
278             */
279            public String getIncludeFiles() {
280                    return incFiles.toString();
281            }
282    
283            /**
284             * インクルードしたファイルのキャãƒ?‚·ュをクリアしますã?
285             * キャãƒ?‚·ュは、インスタンスではなくã?スタãƒ?‚£ãƒ?‚¯変数で管ç�?�—てã�?�¾すã?
286             * よって、ä¸??の処ç�??初めとæœ?¾Œにクリアしておいてくださいã€?
287             *
288             * @og.rev 5.6.7.1 (2013/08/09) 新規追åŠ?
289             */
290            public static void cacheClear() {
291                    includeFiles.clear();
292            }
293    
294            /**
295             * ãƒ?‚¹ト用の main メソãƒ?ƒ‰
296             *
297             * @param       args    コマンド引数配å?
298             */
299            public static void main( final String[] args ) {
300                    JspIncludeReader reader = new JspIncludeReader();
301    //              String xml = reader.getString( new File( args[0] ),"UTF-8",false );
302                    String xml = reader.getString( new File( args[0] ),"UTF-8" );
303    
304                    if( args.length > 1 ) {
305                            java.io.PrintWriter writer = FileUtil.getPrintWriter( new File( args[1] ),"UTF-8" );
306                            writer.print( xml );
307                            Closer.ioClose( writer );
308                    }
309                    else {
310                            System.out.println( xml );
311                    }
312            }
313    }