001    /*
002     * Copyright © 2008, 2009 Pedro Agulló Soliveres.
003     * 
004     * This file is part of DirectJNgine.
005     *
006     * DirectJNgine is free software: you can redistribute it and/or modify
007     * it under the terms of the GNU Lesser General Public License as published by
008     * the Free Software Foundation, either version 3 of the License.
009     *
010     * Commercial use is permitted to the extent that the code/component(s)
011     * do NOT become part of another Open Source or Commercially developed
012     * licensed development library or toolkit without explicit permission.
013     *
014     * DirectJNgine is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017     * GNU Lesser General Public License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public License
020     * along with DirectJNgine.  If not, see <http://www.gnu.org/licenses/>.
021     * 
022     * This software uses the ExtJs library (http://extjs.com), which is 
023     * distributed under the GPL v3 license (see http://extjs.com/license).
024     */
025    
026    package com.softwarementors.extjs.djn.jscodegen;
027    
028    import java.lang.reflect.Method;
029    import java.util.List;
030    
031    import com.softwarementors.extjs.djn.ClassUtils;
032    import com.softwarementors.extjs.djn.api.RegisteredAction;
033    import com.softwarementors.extjs.djn.api.RegisteredApi;
034    import com.softwarementors.extjs.djn.api.RegisteredPollMethod;
035    import com.softwarementors.extjs.djn.api.RegisteredStandardMethod;
036    import com.softwarementors.extjs.djn.config.GlobalConfiguration;
037    import com.softwarementors.extjs.djn.router.processor.poll.PollRequestProcessor;
038    
039    import edu.umd.cs.findbugs.annotations.NonNull;
040    
041    public class ApiCodeGenerator {
042      
043      private static final String REMOTING_TYPE = "remoting";
044      @NonNull private RegisteredApi api;
045      @NonNull private GlobalConfiguration globalConfiguration;
046      
047      public ApiCodeGenerator( GlobalConfiguration globalConfiguration, RegisteredApi api ) {
048        assert globalConfiguration != null;
049        assert api != null;
050        
051        this.globalConfiguration = globalConfiguration;
052        this.api = api;
053      }
054    
055      private static final String TABS_1 = "  ";
056      private static final String TABS_2 = "    ";
057      private static final String TABS_3 = "      ";
058      
059      public void appendCode( StringBuilder result, boolean minify) {
060        assert result != null;
061        
062        if( !minify ) {
063          result.append( "/**********************************************************************\n");
064          result.append( " * \n");
065          result.append( " * Code generated automatically by DirectJNgine\n");
066          result.append( " * Copyright (c) 2009, Pedro Agulló Soliveres\n");
067          result.append( " * \n");
068          result.append( " * DO NOT MODIFY MANUALLY!!\n");
069          result.append( " * \n");
070          result.append( " **********************************************************************/\n");
071          result.append( "\n");
072        }
073        appendNamespaceAndProviderUrlSection(result);
074        appendPollingUrlsSection(result, minify);
075        appendActionsSection(result, minify);
076      }
077    
078      private void appendNamespaceAndProviderUrlSection(StringBuilder result) {
079        assert result != null;
080        
081        result.append( "Ext.namespace( '"); result.append( this.api.getApiNamespace()); result.append("');\n" );
082        if( !this.api.getActionsNamespace().equals("")) {
083          result.append( "Ext.namespace( '"); result.append( this.api.getActionsNamespace()); result.append("');\n" );
084        }
085        result.append('\n');
086        result.append( this.api.getApiNamespace() ); result.append( ".PROVIDER_BASE_URL=" ); appendJsExpressionToGetBaseUrl(result); result.append( this.globalConfiguration.getProvidersUrl() );  result.append( "';");
087        result.append('\n');
088        result.append('\n');
089      }
090    
091      private void appendJsExpressionToGetBaseUrl(StringBuilder result) {
092        assert result != null;
093        
094        String contextPath = this.globalConfiguration.getContextPath();
095        if( contextPath == null ) {
096          String JSCRIPT_CALCULATED_CONTEXT_PATH = "(window.location.pathname.split('/').length>2 ? window.location.pathname.split('/')[1]+ '/' : '')  + '";
097          result.append( "window.location.protocol + '//' + window.location.host + '/' + " + JSCRIPT_CALCULATED_CONTEXT_PATH );
098        }
099        else {
100          if( !contextPath.startsWith("/")) {
101            contextPath = "/" + contextPath;
102          }
103          if( !contextPath.endsWith("/")) {
104            contextPath += "/";
105          }
106          result.append( "window.location.protocol + '//' + window.location.host + '" + contextPath + "' + '" );
107        }
108      }
109    
110      private void appendPollingUrlsSection(StringBuilder result, boolean minify) {
111        assert result != null;
112      
113        result.append( this.api.getApiNamespace() ); result.append( ".POLLING_URLS = {\n");
114        List<RegisteredPollMethod> pollMethods = this.api.getPollMethods();
115        for( int i = 0; i < pollMethods.size(); i++  ) {
116          RegisteredPollMethod pollMethod = pollMethods.get(i);
117          boolean isLast = i == pollMethods.size() - 1;
118          appendPollUrl(result, pollMethod, minify);
119          if( !isLast ) {
120           result.append( ", ");
121          }
122          result.append( "\n");
123        }
124        result.append( "}\n");
125        result.append( "\n");
126      }
127    
128      private void appendPollUrl(StringBuilder result, RegisteredPollMethod pollMethod, boolean minify) {
129        assert result != null;
130        assert pollMethod != null;
131        
132        result.append( TABS_1 ); result.append( pollMethod.getName()); result.append( " : ");
133        result.append( this.api.getApiNamespace() ); result.append( ".PROVIDER_BASE_URL + " );  result.append( "'" ); result.append( PollRequestProcessor.PATHINFO_POLL_PREFIX); result.append( pollMethod.getName() ); result.append( "' " );
134        if( !minify ) {
135          appendPollUrlExtraInformation(result, pollMethod);
136        }
137      }
138    
139      private void appendPollUrlExtraInformation(StringBuilder result, RegisteredPollMethod pollMethod) {
140        result.append( "/* "); appendMethodExtraInformation(pollMethod.getMethod(), pollMethod.getActionClass(), false, result);
141        result.append( " -- calls "); result.append( ClassUtils.getNicePrintableName(pollMethod.getActionClass()) ); result.append( "."); result.append( pollMethod.getMethod().getName() );
142        result.append( " */");
143      }
144    
145      private void appendActionsSection(StringBuilder result, boolean minify) {
146        assert result != null;
147        
148        appendActionsStart(result);    
149        List<RegisteredAction> actions = this.api.getActions();
150        
151        for( int i = 0; i < actions.size(); i++ ) {
152          boolean isLast = i == actions.size() - 1;
153          appendAction( actions.get(i), result, isLast, minify );
154        }
155        appendActionsEnd(result);
156      }
157    
158      private void appendActionsStart( StringBuilder result ) {
159        assert result != null;
160        
161        result.append( this.api.getApiNamespace() ); result.append( ".REMOTING_API"); result.append( " = {\n" );
162        
163        result.append("  url: " ); result.append( this.api.getApiNamespace() ); result.append( ".PROVIDER_BASE_URL") ; result.append( ",\n" );
164        result.append("  type: '" ); result.append( REMOTING_TYPE ); result.append( "',\n" );
165        if( !this.api.getActionsNamespace().equals("")) {
166          result.append( "  namespace: " ); result.append( this.api.getActionsNamespace()); result.append( ",\n");
167        }
168        result.append("  actions: {\n" );
169      }
170    
171      private void appendActionsEnd( StringBuilder result ) {
172        assert result != null;
173        
174        result.append( "  }\n" );
175        result.append( "}\n" );
176        result.append( '\n');
177      }
178      
179      private void appendAction(RegisteredAction action, StringBuilder result, boolean isLast, boolean minify ) {
180        assert action != null;
181        assert result != null;
182      
183        result.append( TABS_2 ); result.append( action.getName() ); result.append( ": [\n");
184        
185        List<RegisteredStandardMethod> methods = action.getStandardMethods();
186        for( int i = 0; i < methods.size(); i++ ) {
187          boolean isLastMethod = i == methods.size() - 1;
188          appendMethod( methods.get(i), result, isLastMethod, minify );
189        }
190        
191        result.append( TABS_2 ); result.append( "]" ); if( !isLast ) { result.append(","); } result.append('\n');
192      }
193    
194      private void appendMethod(RegisteredStandardMethod method, StringBuilder result, boolean isLast, boolean minify) {
195        assert method != null;
196        assert result != null;
197    
198        result.append( TABS_3 ); result.append( "{\n");
199        result.append( TABS_3 ); result.append( "  name: '"); result.append( method.getName() ); result.append( "'" );
200        if( !minify ) {
201          appendMethodExtraInformation(method, result); 
202        }
203        result.append( ",\n");
204        result.append( TABS_3 ); result.append( "  len: "); result.append( method.getClientParameterCount() ); result.append( ",\n");
205        result.append( TABS_3 ); result.append( "  formHandler: "); result.append( method.isFormHandler() ); result.append( "\n");
206        result.append( TABS_3 ); result.append( "}" ); if( !isLast ) { result.append(","); } result.append( '\n');
207      }
208    
209      private void appendMethodExtraInformation(RegisteredStandardMethod method, StringBuilder result) {
210        assert method != null;
211        assert result != null;
212        
213        result.append( "/*");
214    
215        appendMethodExtraInformation( method.getMethod(), method.getActionClass(), !method.isFormHandler(), result );
216    
217        if( method.isFormHandler()) {
218          result.append( " -- FORM HANDLER");
219        }
220        result.append( " */");
221      }
222      
223      private void appendMethodExtraInformation( Method method, Class<?> classOwningMethod, boolean writeJavaParameterTypes, StringBuilder result ) {
224        assert method != null;
225        assert classOwningMethod != null;
226        assert result != null;
227        
228        // Append parameters
229        result.append( "(");
230        if( writeJavaParameterTypes ) {
231          Class<?>[] parameterTypes = method.getParameterTypes();
232          for( int i=0; i < parameterTypes.length; i++ ) {
233            boolean isLast = i == parameterTypes.length - 1;
234            Class<?> cls = parameterTypes[i];
235            result.append( ClassUtils.getNicePrintableName(cls) );
236            if( !isLast ) {
237              result.append( ", ");
238            }
239          }
240        }
241        result.append( ")");
242        
243        // Append result type
244        Class<?> resultType = method.getReturnType();
245        if( !resultType.equals(Void.class)) {
246          result.append( " => ");
247          result.append( ClassUtils.getNicePrintableName(resultType));
248        }
249        
250      }
251    }