001
002package gudusoft.gsqlparser.pp.para;
003
004import java.util.Hashtable;
005import java.util.Map;
006
007public class GFmtOptFactory
008{
009
010        /**
011         * all the instances, the key is the session id.
012         */
013        private volatile static Map<String, GFmtOpt> objs = new Hashtable<String, GFmtOpt>( );
014
015        /**
016         * create an new instance
017         * 
018         * @param sessionId
019         *            the session id
020         * @return the new option instance
021         */
022        public static GFmtOpt newInstance( String sessionId )
023        {
024                if ( !objs.containsKey( sessionId ) )
025                {
026                        synchronized ( GFmtOptFactory.class )
027                        {
028                                if ( !objs.containsKey( sessionId ) )
029                                {
030                                        objs.put( sessionId, new GFmtOpt( sessionId ) );
031                                }
032                        }
033                }
034
035                return objs.get( sessionId );
036        }
037
038        public static GFmtOpt newInstance( )
039        {
040                return new GFmtOpt( uniqueSessionId( ) );
041        }
042
043        /**
044         * A session id no other call gets. The formatter caches are keyed and
045         * cleared by session id, so two concurrent calls that shared one (as
046         * {@code System.nanoTime()} ids did) cleared each other's formatters
047         * mid-format. A UUID is fixed-length, so no id is a prefix of another: the
048         * caches clear by session-id prefix.
049         */
050        private static String uniqueSessionId( )
051        {
052                return "gfmt-" + java.util.UUID.randomUUID( );
053        }
054
055        /**
056         * A registered copy of {@code source}: every public, non-static, non-final
057         * field copied into a new instance registered under a fresh unique session
058         * id. Parts of the pp formatter re-read their options by session id
059         * ({@code getOption()}), so options that are not registered are silently
060         * replaced there by defaults (blank lines, tabs, line numbers, the compact
061         * mode width, block bodies). Callers formatting per request must release
062         * the copy with {@link #remove(String)} when done.
063         *
064         * @param source the options to copy; must not be {@code null}
065         * @return the registered copy
066         */
067        public static GFmtOpt newRegisteredCopy( GFmtOpt source )
068        {
069                if ( source == null ) throw new NullPointerException( "source" );
070                GFmtOpt copy = newInstance( uniqueSessionId( ) );
071                for ( java.lang.reflect.Field f : GFmtOpt.class.getFields( ) )
072                {
073                        int mod = f.getModifiers( );
074                        if ( java.lang.reflect.Modifier.isStatic( mod ) || java.lang.reflect.Modifier.isFinal( mod ) ) continue;
075                        try
076                        {
077                                f.set( copy, f.get( source ) );
078                        }
079                        catch ( IllegalAccessException e )
080                        {
081                                throw new IllegalStateException( "cannot copy GFmtOpt." + f.getName( ), e );
082                        }
083                }
084                return copy;
085        }
086
087        /**
088         * Forget the instance registered under {@code sessionId}. The registry is
089         * otherwise never cleared, so per-request registrations must be removed.
090         *
091         * @param sessionId the id to remove; {@code null} is ignored
092         */
093        public static void remove( String sessionId )
094        {
095                if ( sessionId != null ) objs.remove( sessionId );
096        }
097
098}