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