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 */
016package org.opengion.fukurou.util;
017
018import java.io.BufferedReader;
019import java.io.PrintWriter;
020import java.io.File;
021import java.io.IOException;
022
023/**
024 * CommentLineParser.java は、ファイルを行単位に処理して、コメントを除去するクラスです。
025 * 1行分の文字列を読み取って、コメント部分を削除した文字列を返します。
026 *
027 * ブロックコメントの状態や、コメント除外の状態を管理しています。
028 * オブジェクト作成後、line( String ) メソッドに、ファイルから読み取った1行分の文字列を渡せば、
029 * コメントが除外された形で返されます。
030 * 行として存在しない場合は、null を返します。
031 *
032 * @og.rev 5.7.4.0 (2014/03/07) 新規追加
033 * @og.group ユーティリティ
034 *
035 * @version  6.0
036 * @author       Kazuhiko Hasegawa
037 * @since    JDK7.0,
038 */
039public class CommentLineParser {
040        private String LINE_CMNT   = "//" ;             // ラインコメント
041        private String BLOCK_CMNT1 = "/*" ;             // ブロックコメントの開始
042        private String BLOCK_CMNT2 = "*/" ;             // ブロックコメントの終了
043        private char   ESC_CHAR    = '"'  ;             // コメント除外(プログラム等で使用)
044
045        private boolean escIn   = false;                // コメント除外中かどうか
046        private boolean blockIn = false;                // ブロックコメントが継続しているかどうか
047        private boolean rtnOnly = false;                // 前の行が改行のみだったかどうか。
048
049        /**
050         * コメントの種類を指定します。何も指定しない場合は、Javaコメントを初期値で設定します。
051         *
052         * Javaの場合は、順番に、// , /* , */ , "(二重引用符) になります。
053         * JavaScriptなら、// , <!-- , --> , "
054         * ORACLEなら、-- , /* , */ , "(一重引用符) になります。
055         *
056         * ※ サブクラスで分けてもよかったのですが、とりあえず引数私にしました。
057         *
058         * @og.rev 5.7.4.0 (2014/03/07) 新規追加
059         *
060         * @param       lineCmnt        ラインコメント
061         * @param       blockCmnt1      ブロックコメントの開始
062         * @param       blockCmnt2      ブロックコメントの終了
063         * @param       escChar         コメント除外(プログラム等で使用)
064         */
065        public void init( final String lineCmnt,final String blockCmnt1,final String blockCmnt2,final char escChar ) {
066                LINE_CMNT   = lineCmnt ;                // ラインコメント
067                BLOCK_CMNT1 = blockCmnt1 ;              // ブロックコメントの開始
068                BLOCK_CMNT2 = blockCmnt2 ;              // ブロックコメントの終了
069                ESC_CHAR    = escChar  ;                // コメント除外(プログラム等で使用)
070        }
071
072        /**
073         * 1行分の文字列を読み取って、コメント部分を削除した文字列を返します。
074         * 行として存在しない場合は、null を返します。
075         *
076         * @og.rev 5.7.4.0 (2014/03/07) 新規追加
077         *
078         * @param       inLine 1行の文字列
079         * @return      コメント削除後の1行の文字列
080         */
081        public String line( final String inLine ) {
082                if( inLine == null ) { return null; }
083
084                int size = inLine.length();
085
086                StringBuilder buf = new StringBuilder( size );
087
088                for( int st=0; st<size; st++ ) {
089                        char ch = inLine.charAt(st);
090
091                        // ブロック外で、エスケープ文字の場合は、内外反転
092                        if( !blockIn && ESC_CHAR == ch ) { escIn = !escIn ; }
093
094                        if( !escIn ) {                                                                          // エスケープ外ら、処理を進める
095                                // ブロックコメント継続中
096                                if( blockIn ) {
097                                        int ed = inLine.indexOf( BLOCK_CMNT2,st ) ;             // 終了を見つける
098                                        if( ed >= 0 ) {                                                                 // 終了があれば、そこまで進める。
099                                                blockIn = false;
100                                                st = ed+BLOCK_CMNT2.length();
101                                                continue;                                                                       // ブロックコメント脱出。再読み込み
102                                        }
103                                        break;                                                                                  // ブロックコメント未発見。次の行へ
104                                }
105
106                                // ラインコメント発見。次の行へ
107                                if( inLine.startsWith( LINE_CMNT,st ) ) { break; }      
108
109                                // ブロックコメントを見つける
110                                if( inLine.startsWith( BLOCK_CMNT1,st ) ) {
111                                        int ed = inLine.indexOf( BLOCK_CMNT2,st ) ;             // 終了を見つける
112                                        if( ed >= 0 ) {
113                                                st = ed+BLOCK_CMNT2.length();
114                                                continue;                                                                       // ブロックコメント脱出。再読み込み
115                                        }
116                                        else {
117                                                blockIn = true;
118                                        }
119                                        break;                                                                                  // ブロックコメント未発見。次の行へ
120                                }
121                        }
122
123                        // 通常の文字なので、追加する。
124                        buf.append( ch );
125                }
126
127                // rTrim() と同等の処理
128                int len = buf.length();
129                while( 0 < len && buf.charAt(len-1) <= ' ' ) {
130                        len--;
131                }
132                buf.setLength( len );
133
134                String rtn = null;
135                // 長さが 0 の行は、連続で現れないようにします。
136                if( len == 0 ) {
137                        if( !rtnOnly ) {
138                                rtnOnly = true;
139                                rtn = "";
140                        }
141                }
142                else {
143                        rtnOnly = false;
144                        rtn = buf.toString();
145                }
146
147                return rtn ;
148        }
149
150        /**
151         * このクラスの動作確認用の、main メソッドです。
152         *
153         * Usage: java org.opengion.fukurou.util.CommentLineParser inFile outFile [encode]
154         *
155         * @param       args    コマンド引数配列
156         */
157        public static void main( final String[] args ) {
158                if( args.length < 2 ) {
159                        System.out.println( "Usage: java org.opengion.fukurou.util.CommentLineParser inFile outFile [encode]" );
160                }
161
162                File inFile  = new File( args[0] );
163                File outFile = new File( args[1] );
164                String encode  = (args.length >= 3 ) ? args[3] : "UTF-8" ;
165
166                BufferedReader reader = FileUtil.getBufferedReader( inFile ,encode );
167                PrintWriter    writer = FileUtil.getPrintWriter(   outFile ,encode );
168
169                CommentLineParser clp = new CommentLineParser();
170
171                try {
172                        String line1;
173                        while((line1 = reader.readLine()) != null) {
174                                line1 = clp.line( line1 );
175                                if( line1 != null ) {
176                                        writer.println( line1 );
177                                }
178                        }
179                }
180                catch( IOException ex ) {
181                        String errMsg = "ファイルコピー中に例外が発生しました。\n"
182                                                + " inFile=[" + inFile + "] , outFile=[" + outFile + "]\n" ;
183                        throw new RuntimeException( errMsg,ex );
184                }
185                finally {
186                        Closer.ioClose( reader ) ;
187                        Closer.ioClose( writer ) ;
188                }
189        }
190}