001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.text.csv;
020
021import java.util.ArrayList;
022import java.util.List;
023
024import lucee.commons.lang.StringUtil;
025
026
027public class CSVString {
028
029        private static final char LF=10;
030        private static final char CR=13;
031        
032    private char[] buffer;
033    private int pos;
034    private char delim;
035
036    public CSVString( String input, char delim ) {
037        this.buffer = input.toCharArray();
038        this.delim = delim;
039    }
040
041    public List<List<String>> parse() {
042
043        List<List<String>> result = new ArrayList();
044        List<String> line = new ArrayList();
045
046        if ( buffer.length == 0 )
047            return result;
048
049        StringBuilder sb = new StringBuilder();
050        char c;
051
052        do {
053
054            c = buffer[ pos ];
055            if ( c == '"' || c == '\'' ) {
056                sb.append( fwdQuote( c ) );
057            }
058            else if ( c == LF || c == CR ) {
059                if(c == CR && isNext(LF)) next();
060                line.add( sb.toString().trim() );
061                sb = new StringBuilder();
062                if ( isValidLine( line ) )
063                    result.add( line );
064                line = new ArrayList<String>();
065            }
066            else if ( c == delim ) {
067
068                line.add( sb.toString().trim() );
069                sb = new StringBuilder();
070            }
071            else
072                sb.append( c );
073
074            next();
075        } while ( pos < buffer.length);
076
077        
078        line.add( sb.toString() );
079
080        if ( isValidLine( line ) )
081            result.add( line );
082        return result;
083    }
084
085
086    /** forward pos until the end of quote */
087    StringBuilder fwdQuote( char q ) {
088
089        StringBuilder sb = new StringBuilder();
090
091        while ( hasNext() ) {
092
093            next();
094            sb.append( buffer[ pos ] );
095
096            if ( isCurr( q ) ) {
097                if ( isNext( q ) ) {            // consecutive quote sign
098                    next();
099                }
100                else {
101                    break;
102                }
103            }
104        }
105
106        if ( sb.length() > 0 )
107            sb.setLength( sb.length() - 1 );    // remove closing quote sign
108
109        return sb;
110    }
111
112    void next() {
113
114        pos++;
115    }
116
117    boolean hasNext() {
118
119        return pos < ( buffer.length - 1 );
120    }
121
122    boolean isNext( char c ) {
123
124        if ( !hasNext() )   return false;
125
126        return buffer[ pos + 1 ] == c;
127    }
128
129
130    boolean isCurr( char c ) {
131
132        if ( !isValidPos() )
133            return false;
134
135        return buffer[ pos ] == c;
136    }
137
138
139    boolean isValidPos() {
140
141        return pos >= 0 && pos < buffer.length - 1;
142    }
143
144
145    boolean isValidLine( List<String> line ) {
146
147        for ( String s : line ) {
148
149            if ( !StringUtil.isEmpty( s, true ) )
150                return true;
151        }
152
153        return false;
154    }
155
156}