content: /*
  * Copyright (c) 1997 - 2016
  * Actelion Pharmaceuticals Ltd.
  * Gewerbestrasse 16
  * CH-4123 Allschwil, Switzerland
  *
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
  *
  * 1. Redistributions of source code must retain the above copyright notice, this
  *    list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
  * 3. Neither the name of the the copyright holder nor the
  *    names of its contributors may be used to endorse or promote products
  *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
  
  package com.actelion.research.io;
  
  import com.actelion.research.util.ConstantsDWAR;
  import com.actelion.research.util.Pipeline;
  
  import java.io.IOException;
  import java.io.UnsupportedEncodingException;
  import java.nio.Buffer;
  import java.nio.ByteBuffer;
  import java.nio.channels.ReadableByteChannel;
  import java.util.Arrays;
  
  /**
   *
   * StringReadChannel
   * 2007 MvK: Start implementation
   * 25.06.2009 MvK: implementation changed
   * 12.02.2014 MvK: added charset encoding to handle Umlaute.
   * 24.04.2014 MvK: Pipeline replaced simple LinkedList because of needed concurrent access.
   * 29.01.2015 MvK: Increased capacity CAPACITY_LINE_BUFFER to 10,000,000 because of overflow when reading PubMed records.
   * 03.06.2015 MvK: Increased capacity CAPACITY_LINE_BUFFER to 50,000,000 because of overflow when reading g2dDiseasePublicationSlope.dwar
   */
  public class StringReadChannel {
  
  	private static final int CAPACITY_LINE_BUFFER = 50000000;
  	
  	private static final int CAPACITY_READ_BUFFER = 100000;
  	
  	private static final int CAPACITY_PIPE = 1000;
  	
  	
  	
  	private ReadableByteChannel byteChannel;
  		
  	private ByteBuffer buffer;
  	
  	private ByteBuffer byteBufferLine;
  	
  	private Pipeline<String> pipeline;
  		
  	public StringReadChannel(ReadableByteChannel ch)throws IOException{
  		init(ch);
  	}
  	
  	private void init(ReadableByteChannel bc) throws IOException{
  		
  		byteChannel = bc;
  		
  
  		// We will fill up this ByteBuffer instance later.
  		byteBufferLine = ByteBuffer.allocate(CAPACITY_LINE_BUFFER);
  		
  		buffer = ByteBuffer.allocate(CAPACITY_READ_BUFFER);
  		
  		pipeline = new Pipeline<String>();
  		
  		readLine2List();
  	}
  	
  	public boolean hasMoreLines() throws IOException {
  		
  		return !pipeline.wereAllDataFetched();
  		
  	}
  	
  	/**
  	 * 
  	 * @return null if EOF reached.
  	 * @throws IOException
  	 */
  	public String readLine() throws IOException {
  
  		int maxCycles = 100;
  		
  		String str = null;
  		
  		
  		if(!pipeline.isEmpty()){
  			
  			str = pipeline.pollData();
  			
  		} else if(!pipeline.isAllDataIn()){
  			
  			int ccCycle=0;
  			
  			while(pipeline.isEmpty()){
  			
  				try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}
  			
  				ccCycle++;
  				
  				if(ccCycle>maxCycles){
  					
  					RuntimeException ex = new RuntimeException("Arbitrary break. Max number of cycles (" + maxCycles + ") exceeded.");
  					
  					// ex.printStackTrace();
  					
  					byteChannel.close();
  					
  					throw ex;
  				}
  				
  				str = pipeline.pollData();
  			}
  		}
  		
  		
  		if(!pipeline.isAllDataIn()){
  			if(pipeline.sizePipe() < CAPACITY_PIPE){
  				readLine2List();	
  			}
  			
  			
  		}
  		
  		
  		return str;
  	}
  	
  	public void finish()throws IOException{
  		close();
  	}
  	
  	private int readLine2List() throws IOException {
  				
  		boolean lineFinished=false;
  		
  		int sizeLine = 0;
  		
  		while(!lineFinished) {
  			
  			((Buffer)buffer).clear();
  			
  			int size = byteChannel.read(buffer);
  					
  			if(size==-1){ // end of stream
  				
  				if(byteBufferLine.position() > 0){
  				
  					writeBuffer2Pipe();
  					lineFinished = true;
  					
  				}
  				
  				pipeline.setAllDataIn(true);
  							
  				return -1;
  			}
  			
  			//
  			// Copying from one buffer to the other
  			//
  			for (int i = 0; i < size; i++) {
  				
  				byte c = buffer.get(i);
  							
  				if(c=='\n') {
  										
  					writeBuffer2Pipe();
  						
  					lineFinished = true;
  					
  				} else {
  					if(c!='\r') {
  					
  						byteBufferLine.put(c);
  						
  						sizeLine++;
  						
  					}
  				}
  			}
  			
  		}
  		
  		return sizeLine;
  	}
  	
  	private void writeBuffer2Pipe() throws UnsupportedEncodingException{
  		
  		byte [] contentsOnly = Arrays.copyOf(byteBufferLine.array(), byteBufferLine.position());
  		
  		String str = new String(contentsOnly, ConstantsDWAR.CHARSET_ENCODING);
  		
  		StringBuilder sb = new StringBuilder(str);
  		
  		pipeline.addData(sb.toString());
  		
  		((Buffer)byteBufferLine).clear();
  	}
  	
  	
  	
  //	private int readLine2List() throws IOException {
  //		
  //		ByteBuffer buffer = ByteBuffer.allocate(CAPACITY);
  //		
  //		int size = byteChannel.read(buffer);
  //				
  //		if(VERBOSE){
  //			System.out.println("StringReadChannel readLine2List() size " + size);
  //		}
  //		
  //		
  //		if(size==-1){
  //			
  //			if(byteBufferLine.position() > 0){
  //			
  //				byte [] contentsOnly = Arrays.copyOf(byteBufferLine.array(), byteBufferLine.position());
  //							
  //				String str = new String(contentsOnly, ConstantsDWAR.CHARSET_ENCODING);
  //				
  //				StringBuilder sb = new StringBuilder(str);
  //				
  //				liLine.add(sb);
  //			
  //			}
  //			
  //			bEOF = true;
  //			return -1;
  //		}
  //		
  //		for (int i = 0; i < size; i++) {
  //			byte c = buffer.get(i);
  //			
  //			if(c==-1) {
  //				bEOF=true;
  //			} else {
  //				if(c=='\n') {
  //										
  //					byte [] contentsOnly = Arrays.copyOf(byteBufferLine.array(), byteBufferLine.position());
  //					
  //					byteBufferLine.clear();
  //					
  //					String str = new String(contentsOnly, ConstantsDWAR.CHARSET_ENCODING);
  //					
  //					StringBuilder sb = new StringBuilder(str);
  //					
  //					liLine.add(sb);
  //										
  //				} else {
  //					if(c!='\r') {
  //					
  //						byteBufferLine.put(c);
  //											
  //					}
  //				}
  //			}
  //		}
  //		
  //		return size;
  //	}
  	
      public static void skipUntilLineMatchesRegEx(StringReadChannel src, String regex) throws NoSuchFieldException, IOException {
      	int limit = 10000;
      	
      	skipUntilLineMatchesRegEx(src, regex, limit);
      }
      
      public static String skipUntilLineMatchesRegEx(StringReadChannel src, String regex, int limit) throws NoSuchFieldException, IOException {
      	    	    		
      	String line = src.readLine();
      	
      	boolean match = false;
      	
      	if(line.matches(regex)){
      		match = true;
      	}
      	
      	int cc=0;
      	
  		while(!match){
      		
  			if(!src.hasMoreLines()){
  				break;
  			}
  			
  			line = src.readLine();
  			
  	    	if(line.matches(regex)){
  	    		match = true;
  	    	} 
  			
  	    	cc++;
  	    	
  	    	if(cc > limit){
  	    		break;
  	    	}
  	    	
      		
      	}	
  		
  		String lineMatch = null;
  		
  		if(match){
  			
  			lineMatch = line;
  			
  		} else {
  			throw new NoSuchFieldException("Regex " + regex + " was not found.");
  		}
      	
  		return lineMatch;
  		
      	
      }
  
      public void close() throws IOException {
      	byteChannel.close();
      }
  
  }
  