001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.api.ldap.schema.converter; 021 022 023import java.io.ByteArrayInputStream; 024import java.io.File; 025import java.io.IOException; 026import java.io.InputStream; 027import java.io.PipedInputStream; 028import java.io.PipedOutputStream; 029import java.nio.file.Files; 030import java.nio.file.Paths; 031import java.text.ParseException; 032import java.util.List; 033 034import org.apache.commons.lang.exception.ExceptionUtils; 035import org.apache.directory.api.i18n.I18n; 036import org.apache.directory.api.util.Strings; 037 038import antlr.RecognitionException; 039import antlr.TokenStreamException; 040 041 042/** 043 * A reusable wrapper for antlr generated schema parsers. 044 * 045 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 046 */ 047public class SchemaParser 048{ 049 /** The antlr generated parser */ 050 private antlrSchemaConverterParser parser = null; 051 052 /** A pipe into the parser */ 053 private PipedOutputStream parserIn = null; 054 055 /** A temporary buffer storing the read schema bytes */ 056 private byte[] buf = new byte[128]; 057 058 /** The inputStream mapped over the schema file to parse */ 059 private InputStream schemaIn; 060 061 /** The thread used to read the schema */ 062 private Thread producerThread; 063 064 065 /** 066 * Creates a reusable instance of an SchemaParser. 067 * 068 * @throws java.io.IOException if the pipe cannot be formed 069 */ 070 public SchemaParser() throws IOException 071 { 072 init(); 073 } 074 075 076 /** 077 * Initializes a parser and its plumbing. 078 * 079 * @throws java.io.IOException if a pipe cannot be formed. 080 */ 081 public synchronized void init() throws IOException 082 { 083 parserIn = new PipedOutputStream(); 084 PipedInputStream in = new PipedInputStream(); 085 parserIn.connect( in ); 086 antlrSchemaConverterLexer lexer = new antlrSchemaConverterLexer( in ); 087 parser = new antlrSchemaConverterParser( lexer ); 088 } 089 090 091 /** 092 * Clear the parser. 093 */ 094 public synchronized void clear() 095 { 096 parser.clear(); 097 } 098 099 100 /** 101 * Thread safe method parses an OpenLDAP schemaObject element/object. 102 * 103 * @param schemaObject the String image of a complete schema object 104 * @return The list of parsed schema elements 105 * @throws java.io.IOException If the schema file can't be processed 106 * @throws java.text.ParseException If we weren't able to parse the schema 107 */ 108 public synchronized List<SchemaElement> parse( String schemaObject ) throws IOException, ParseException 109 { 110 if ( ( schemaObject == null ) || ( schemaObject.trim().equals( Strings.EMPTY_STRING ) ) ) 111 { 112 throw new ParseException( I18n.err( I18n.ERR_06001_EMPTY_OR_NULL_SCHEMA_OBJECT ), 0 ); 113 } 114 115 schemaIn = new ByteArrayInputStream( Strings.getBytesUtf8( schemaObject ) ); 116 117 if ( producerThread == null ) 118 { 119 producerThread = new Thread( new DataProducer() ); 120 } 121 122 producerThread.start(); 123 124 return invokeParser( schemaObject ); 125 } 126 127 128 /** 129 * Invoke the parser 130 * 131 * @param schemaName The schema to be parsed 132 * @return A list of schema elements 133 * @throws java.io.IOException If the schema file can't be processed 134 * @throws java.text.ParseException If we weren't able to parse the schema 135 */ 136 private List<SchemaElement> invokeParser( String schemaName ) throws IOException, ParseException 137 { 138 try 139 { 140 parser.parseSchema(); 141 142 return parser.getSchemaElements(); 143 } 144 catch ( RecognitionException re ) 145 { 146 String msg = I18n.err( I18n.ERR_06002_PARSER_FAILURE, schemaName, ExceptionUtils.getFullStackTrace( re ) ); 147 init(); 148 throw new ParseException( msg, re.getColumn() ); 149 } 150 catch ( TokenStreamException tse ) 151 { 152 String msg = I18n.err( I18n.ERR_06002_PARSER_FAILURE, schemaName, ExceptionUtils.getFullStackTrace( tse ) ); 153 init(); 154 throw new ParseException( msg, 0 ); 155 } 156 } 157 158 159 /** 160 * Thread safe method parses a stream of OpenLDAP schemaObject elements/objects. 161 * 162 * @param schemaIn a stream of schema objects 163 * @return A list of schema elements 164 * @throws java.io.IOException If the schema file can't be processed 165 * @throws java.text.ParseException If we weren't able to parse the schema 166 */ 167 public synchronized List<SchemaElement> parse( InputStream schemaIn ) throws IOException, ParseException 168 { 169 this.schemaIn = schemaIn; 170 171 if ( producerThread == null ) 172 { 173 producerThread = new Thread( new DataProducer() ); 174 } 175 176 producerThread.start(); 177 178 return invokeParser( "schema input stream ==> " + schemaIn.toString() ); 179 } 180 181 182 /** 183 * Thread safe method parses a file of OpenLDAP schemaObject elements/objects. 184 * 185 * @param schemaFile a file of schema objects 186 * @throws java.io.IOException If the schema file can't be processed 187 * @throws java.text.ParseException If we weren't able to parse the schema 188 */ 189 public synchronized void parse( File schemaFile ) throws IOException, ParseException 190 { 191 schemaIn = Files.newInputStream( Paths.get( schemaFile.getPath() ) ); 192 193 if ( producerThread == null ) 194 { 195 producerThread = new Thread( new DataProducer() ); 196 } 197 198 producerThread.start(); 199 invokeParser( "schema file ==> " + schemaFile.getAbsolutePath() ); 200 } 201 202 203 /** 204 * The thread which read the schema files and fill the 205 * temporary buffer used by the lexical analyzer. 206 */ 207 private class DataProducer implements Runnable 208 { 209 /** 210 * {@inheritDoc} 211 */ 212 @Override 213 public void run() 214 { 215 int count = -1; 216 217 try 218 { 219 while ( ( count = schemaIn.read( buf ) ) != -1 ) 220 { 221 parserIn.write( buf, 0, count ); 222 parserIn.flush(); 223 } 224 225 // using an input termination token END - need extra space to return 226 parserIn.write( Strings.getBytesUtf8( "END " ) ); 227 } 228 catch ( IOException e ) 229 { 230 e.printStackTrace(); 231 } 232 } 233 } 234}