/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.indexes.serialization.avro.impl;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.avro.Protocol;
import org.hibernate.search.indexes.serialization.avro.logging.impl.Log;
import org.hibernate.search.util.impl.Closeables;
import org.hibernate.search.util.impl.StreamHelper;
import org.hibernate.search.util.logging.impl.LoggerFactory;
/**
* Helper to build an Avro Protocol version 1.0 from all our resource
* schemas.
*/
class ProtocolBuilderV1_0 {
private static final String AVRO_SCHEMA_FILE_SUFFIX = ".avro";
private static final String AVRO_PROTOCOL_FILE_SUFFIX = ".avpr";
private static String V1_0_PATH = "org/hibernate/search/remote/codex/avro/v1_0/";
private static final Log log = LoggerFactory.make( Log.class );
private final Map<String, String> schemas = new HashMap<String, String>();
/**
* @return an Avro Protocol at version 1.0
*/
Protocol build() {
parseSchema( "attribute/TokenTrackingAttribute" );
parseSchema( "attribute/CharTermAttribute" );
parseSchema( "attribute/PayloadAttribute" );
parseSchema( "attribute/KeywordAttribute" );
parseSchema( "attribute/PositionIncrementAttribute" );
parseSchema( "attribute/FlagsAttribute" );
parseSchema( "attribute/TypeAttribute" );
parseSchema( "attribute/OffsetAttribute" );
parseSchema( "field/TermVector" );
parseSchema( "field/Index" );
parseSchema( "field/Store" );
parseSchema( "field/TokenStreamField" );
parseSchema( "field/ReaderField" );
parseSchema( "field/StringField" );
parseSchema( "field/BinaryField" );
parseSchema( "field/NumericIntField" );
parseSchema( "field/NumericLongField" );
parseSchema( "field/NumericFloatField" );
parseSchema( "field/NumericDoubleField" );
parseSchema( "field/CustomFieldable" );
parseSchema( "Document" );
parseSchema( "operation/Id" );
parseSchema( "operation/OptimizeAll" );
parseSchema( "operation/PurgeAll" );
parseSchema( "operation/Delete" );
parseSchema( "operation/Add" );
parseSchema( "operation/Update" );
parseSchema( "Message" );
return parseProtocol( "Works" );
}
protected final void parseSchema(String filename) {
String fullFileName = getResourceBasePath() + filename + AVRO_SCHEMA_FILE_SUFFIX;
String schema = avroResourceAsString( fullFileName );
schemas.put( filename, schema );
}
protected String getResourceBasePath() {
return V1_0_PATH;
}
protected final Protocol parseProtocol(String name) {
String fullFileName = getResourceBasePath() + name + AVRO_PROTOCOL_FILE_SUFFIX;
String protocolSkeleton = avroResourceAsString( fullFileName );
String protocol = inlineSchemas( protocolSkeleton );
return Protocol.parse( protocol );
}
private String inlineSchemas(String protocolSkeleton) {
String result = protocolSkeleton;
for ( Map.Entry<String, String> entry : schemas.entrySet() ) {
result = replace(
result, "`" + entry.getKey() + "`",
entry.getValue()
);
}
return result;
}
private static String replace(String str, String pattern, String replace) {
int s = 0;
int e;
StringBuilder result = new StringBuilder();
while ( ( e = str.indexOf( pattern, s ) ) >= 0 ) {
result.append( str.substring( s, e ) );
result.append( replace );
s = e + pattern.length();
}
result.append( str.substring( s ) );
return result.toString();
}
private static String avroResourceAsString(String resourceName) {
// using class loader of AvroSerializationProvider, because we load resources included in the same artifact.
InputStream inputStream = AvroSerializationProvider.class.getClassLoader().getResourceAsStream( resourceName );
if ( inputStream == null ) {
throw log.unableToLoadAvroSchema( resourceName );
}
String resource;
try {
resource = StreamHelper.readInputStream( inputStream );
}
catch (IOException e) {
throw log.unableToLoadResource( resourceName );
}
finally {
Closeables.closeQuietly( inputStream );
}
return resource;
}
}