package com.ptc.rbinfohandler; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; import org.netbeans.modules.java.preprocessorbridge.spi.VirtualSourceProvider; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; /** Implementation of VirtualSourceProvider service interface that handles PTC * rbInfo files, producing Java source stubs for these on demand for NetBeans' * source parsing, code completion, etc, mechanisms. * * @author jessh */ public final class RbInfoHandler implements VirtualSourceProvider { private static final String RBINFO_CONSTANT_SUFFIX = ".constant"; private static final int RBINFO_CONSTANT_SUFFIX_LEN = RBINFO_CONSTANT_SUFFIX.length(); private static final Logger logger = Logger.getLogger( RbInfoHandler.class.getName() ); private static final Set supportedExtensions = Collections.singleton( "rbInfo" ); // extensions do not include the "." for this API // Implementation of VirtualSourceProvider method public Set getSupportedExtensions() { return ( supportedExtensions ); } // Implementation of VirtualSourceProvider method public boolean index() { return ( true ); // do index these files (is this right/desirable???) } // Implementation of VirtualSourceProvider method public void translate( final Iterable rbInfoFiles, final File sourceRoot, final Result results ) { logger.finest( "Translate called" ); final FileObject sourceRootFO = FileUtil.toFileObject( sourceRoot ); int nRbInfos = 0; for ( File rbInfoFile : rbInfoFiles ) { final FileObject rbInfoFO = FileUtil.toFileObject( rbInfoFile ); String classPkg = FileUtil.getRelativePath( sourceRootFO, rbInfoFO.getParent() ); final StringBuilder buffer = new StringBuilder( 1024 ); if ( classPkg != null ) { classPkg = classPkg.replace( '/', '.' ); buffer.append( "package " ); buffer.append( classPkg ); buffer.append( ';' ); } else classPkg = ""; final String classLeafname = rbInfoFO.getName(); // doesn't include extension buffer.append( "public class " ); buffer.append( classLeafname ); buffer.append( " extends java.util.ListResourceBundle {" ); final Properties rbInfoProps = getProperties( rbInfoFile ); for ( Map.Entry propEntry : rbInfoProps.entrySet() ) { final String propKey = (String) propEntry.getKey(); if ( !propKey.endsWith( RBINFO_CONSTANT_SUFFIX ) ) continue; final String constantName = (String) propEntry.getValue(); buffer.append( "public static final String " ); buffer.append( constantName ); buffer.append( "=\"" ); final String constantValue = propKey.substring( 0, propKey.length() - RBINFO_CONSTANT_SUFFIX_LEN ); buffer.append( escape( constantValue ) ); buffer.append( "\";" ); } buffer.append( "public Object[][] getContents() {return null;} }" ); // getContents()'s return value is irrelevant for our purposes here if ( logger.isLoggable( Level.FINER ) ) { logger.finer( "Adding results for rbInfo: " + rbInfoFile ); if ( logger.isLoggable( Level.FINEST ) ) { logger.finest( "Package: " + classPkg ); logger.finest( "Class: " + classLeafname ); logger.finest( "Source: " + buffer ); } } results.add( rbInfoFile, classPkg, classLeafname, buffer ); ++nRbInfos; } if ( nRbInfos > 0 ) if ( logger.isLoggable( Level.FINE ) ) logger.fine( "Processed " + nRbInfos + " rbInfo files for " + sourceRoot ); } private static final Pattern backslashPattern = Pattern.compile( "\\\\" ); private static final Pattern doublequotePattern = Pattern.compile( "\"" ); private static String escape( final String string ) { if ( string == null ) return ( null ); return ( doublequotePattern.matcher( backslashPattern.matcher( string ).replaceAll( "\\\\\\\\" ) ).replaceAll( "\\\\\"" ) ); } /** Internal utility that retrieves properties from a file. Rather than throwing * any IOExceptions, this utility will simply return an empty Properties object. */ private static Properties getProperties( final File propsFile ) { final Properties props = new Properties(); InputStream inputStream = null; try { inputStream = new FileInputStream( propsFile ); inputStream = new BufferedInputStream( inputStream ); props.load( inputStream ); } catch ( IOException e ) { logger.log( Level.WARNING, "Failed to read properties from " + propsFile, e ); } finally { if ( inputStream != null ) try { inputStream.close(); } catch ( IOException e ) { logger.log( Level.WARNING, "Failed to close properties file " + propsFile, e ); } } return ( props ); } }