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}