001/*
002  GRANITE DATA SERVICES
003  Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005  This file is part of Granite Data Services.
006
007  Granite Data Services is free software; you can redistribute it and/or modify
008  it under the terms of the GNU Library General Public License as published by
009  the Free Software Foundation; either version 2 of the License, or (at your
010  option) any later version.
011
012  Granite Data Services is distributed in the hope that it will be useful, but
013  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015  for more details.
016
017  You should have received a copy of the GNU Library General Public License
018  along with this library; if not, see <http://www.gnu.org/licenses/>.
019*/
020
021package org.granite.builder;
022
023import java.io.File;
024import java.io.IOException;
025import java.net.MalformedURLException;
026import java.net.URL;
027import java.net.URLClassLoader;
028import java.util.ArrayList;
029import java.util.Collections;
030import java.util.HashMap;
031import java.util.List;
032import java.util.Map;
033
034import org.eclipse.core.resources.IProject;
035import org.eclipse.core.runtime.CoreException;
036import org.eclipse.jdt.core.IJavaProject;
037import org.eclipse.jdt.core.JavaCore;
038import org.granite.builder.properties.Gas3Classpath;
039import org.granite.builder.properties.Gas3Project;
040import org.granite.builder.properties.Gas3Source;
041import org.granite.builder.properties.Gas3Translator;
042import org.granite.builder.properties.GraniteProperties;
043import org.granite.builder.properties.GranitePropertiesLoader;
044import org.granite.builder.util.BuilderUtil;
045import org.granite.builder.util.JavaClassInfo;
046import org.granite.builder.util.ProjectUtil;
047import org.granite.builder.util.ProjectUtil.CpEntry;
048import org.granite.builder.util.ProjectUtil.CpEntry.CpeKind;
049import org.granite.generator.Listener;
050import org.granite.generator.TemplateUri;
051import org.granite.generator.as3.As3TypeFactory;
052import org.granite.generator.as3.DefaultAs3TypeFactory;
053import org.granite.generator.as3.DefaultEntityFactory;
054import org.granite.generator.as3.EntityFactory;
055import org.granite.generator.as3.JavaAs3GroovyConfiguration;
056import org.granite.generator.as3.JavaAs3Input;
057import org.granite.generator.as3.PackageTranslator;
058import org.granite.generator.as3.RemoteDestinationFactory;
059import org.granite.generator.as3.reflect.JavaType.Kind;
060import org.granite.generator.gsp.GroovyTemplateFactory;
061
062/**
063 * @author Franck WOLFF
064 */
065public class BuilderConfiguration implements JavaAs3GroovyConfiguration {
066
067        private final IProject project;
068    private final Map<Class<?>, Boolean> generatedClassCache = new HashMap<Class<?>, Boolean>();
069        private final Map<String, BuilderConfiguration> dependentProjectConfigurations = new HashMap<String, BuilderConfiguration>();
070    
071        private IJavaProject javaProject = null;
072    private GraniteProperties properties = null;
073    private As3TypeFactory clientTypeFactory = null;
074    private EntityFactory entityFactory = null;
075    private RemoteDestinationFactory remoteDestinationFactory = null;
076    private GroovyTemplateFactory groovyTemplateFactory = null;
077    private ClassLoader loader = null;
078    private List<PackageTranslator> translators = null;
079    private Listener listener = null;
080        
081        public BuilderConfiguration(Listener listener, IProject project) {
082                this.listener = listener;
083                this.project = project;
084        }
085
086        public boolean isOutdated() {
087                return GranitePropertiesLoader.isOutdated(project, getProperties());
088        }
089        
090        public IProject getProject() {
091                return project;
092        }
093
094        public IJavaProject getJavaProject() {
095                if (javaProject == null) {
096                        javaProject = JavaCore.create(project);
097                if (javaProject == null) {
098                        throw new RuntimeException(
099                                new CoreException(ProjectUtil.createErrorStatus("Not a Java Project: " + project, null))
100                    );
101                }
102                }
103                return javaProject;
104        }
105
106        public GraniteProperties getProperties() {
107        if (properties == null) {
108                try {
109                        properties = GranitePropertiesLoader.load(project);
110                } catch (IOException e) {
111                        throw new RuntimeException(
112                        new CoreException(
113                                ProjectUtil.createErrorStatus("Could not load Granite properties for: " + project,
114                                e
115                        ))
116                    );
117            }
118        }
119        return properties;
120        }
121        
122        @Override
123        public As3TypeFactory getAs3TypeFactory() {
124                if (clientTypeFactory == null) {
125                        String factoryClass = getProperties().getGas3().getAs3TypeFactory();
126                        if (factoryClass != null) {
127                                try {
128                                        clientTypeFactory = BuilderUtil.newInstance(As3TypeFactory.class, factoryClass, getClassLoader());
129                                        clientTypeFactory.configure(
130                                                getProperties().getGas3().isExternalizeLong(),
131                                                getProperties().getGas3().isExternalizeBigInteger(),
132                                                getProperties().getGas3().isExternalizeBigDecimal()
133                                        );
134                                } catch (Exception e) {
135                                        throw new RuntimeException(
136                                new CoreException(
137                                        ProjectUtil.createErrorStatus("Could not load As3TypeFactory class: " + factoryClass,
138                                        e
139                                ))
140                            );
141                                }
142                        }
143                        else
144                                clientTypeFactory = new DefaultAs3TypeFactory();
145                }
146                return clientTypeFactory;
147        }
148        
149        @Override
150        public EntityFactory getEntityFactory() {
151                if (entityFactory == null) {
152                        String factoryClass = getProperties().getGas3().getEntityFactory();
153                        if (factoryClass != null) {
154                                try {
155                                        entityFactory = BuilderUtil.newInstance(EntityFactory.class, factoryClass, getClassLoader());
156                                } catch (Exception e) {
157                                        throw new RuntimeException(
158                                new CoreException(
159                                        ProjectUtil.createErrorStatus("Could not load EntityFactory class: " + factoryClass,
160                                        e
161                                ))
162                            );
163                                }
164                        }
165                        else
166                                entityFactory = new DefaultEntityFactory();
167                }
168                return entityFactory;
169        }
170        
171        @Override
172        public RemoteDestinationFactory getRemoteDestinationFactory() {
173                if (remoteDestinationFactory == null) {
174                        String factoryClass = getProperties().getGas3().getRemoteDestinationFactory();
175                        if (factoryClass != null) {
176                                try {
177                                        remoteDestinationFactory = BuilderUtil.newInstance(RemoteDestinationFactory.class, factoryClass, getClassLoader());
178                                } catch (Exception e) {
179                                        throw new RuntimeException(
180                                new CoreException(
181                                        ProjectUtil.createErrorStatus("Could not load RemoteDestinationFactory class: " + factoryClass,
182                                        e
183                                ))
184                            );
185                                }
186                        }
187                }
188                return remoteDestinationFactory;
189        }
190
191        @Override
192        public File getBaseOutputDir(JavaAs3Input input) {
193                BuilderJavaClientInput builderInput = (BuilderJavaClientInput)input;
194                return new File(ProjectUtil.getProjectFile(project), builderInput.getGas3Source().getBaseOutputDir(true));
195        }
196
197        @Override
198        public File getOutputDir(JavaAs3Input input) {
199                BuilderJavaClientInput builderInput = (BuilderJavaClientInput)input;
200                return new File(ProjectUtil.getProjectFile(project), builderInput.getGas3Source().getOutputDir());
201        }
202
203        @Override
204        public TemplateUri[] getTemplateUris(Kind kind, Class<?> clazz) {
205                return getProperties().getGas3().getMatchingTemplateUris(kind);
206        }
207
208        @Override
209        public List<PackageTranslator> getTranslators() {
210                if (translators == null) {
211                        if (getProperties().getGas3().getTranslators().isEmpty())
212                                translators = Collections.emptyList();
213                        else {
214                                translators = new ArrayList<PackageTranslator>(getProperties().getGas3().getTranslators().size());
215                                for (Gas3Translator translator : getProperties().getGas3().getTranslators())
216                                        translators.add(translator.getPackageTranslator());
217                        }
218                }
219                return translators;
220        }
221        
222        public PackageTranslator getPackageTranslator(String packageName) {
223        PackageTranslator translator = null;
224
225        int weight = 0;
226        for (PackageTranslator t : getTranslators()) {
227            int w = t.match(packageName);
228            if (w > weight) {
229                weight = w;
230                translator = t;
231            }
232        }
233                
234        return translator;
235        }
236
237        @Override
238        public String getUid() {
239                return getProperties().getGas3().getUid();
240        }
241
242        @Override
243        public boolean isGenerated(Class<?> clazz) {
244                if (!getClassLoader().equals(clazz.getClassLoader()) || (clazz.isMemberClass() && !clazz.isEnum()))
245                        return false;
246                
247                Boolean generated = generatedClassCache.get(clazz);
248                if (generated == null) {
249                        generated = Boolean.FALSE;
250                        
251                        JavaClassInfo info = ProjectUtil.getJavaClassInfo(getJavaProject(), clazz);
252                        if (info != null) {
253                                Gas3Source source = getProperties().getGas3().getMatchingSource(
254                                info.getSourceFolderPath(),
255                                info.getSourceFilePath()
256                        );
257                                generated = Boolean.valueOf(source != null);
258                        }
259                        else {
260                                for (Gas3Project gas3Project : getProperties().getGas3().getProjects()) {
261                                        IProject dependentProject = ProjectUtil.getProject(getJavaProject().getProject(), gas3Project.getPath());
262                                        try {
263                                                if (ProjectUtil.isGraniteProject(dependentProject)) {
264                                                        BuilderConfiguration configuration = dependentProjectConfigurations.get(dependentProject.getName());
265                                                        if (configuration == null) {
266                                                                configuration = new BuilderConfiguration(listener, dependentProject);
267                                                                dependentProjectConfigurations.put(dependentProject.getName(), configuration);
268                                                        }
269                                                        info = ProjectUtil.getJavaClassInfo(configuration.getJavaProject(), clazz);
270                                                        if (info != null) {
271                                                                Gas3Source source = configuration.getProperties().getGas3().getMatchingSource(
272                                                                info.getSourceFolderPath(),
273                                                                info.getSourceFilePath()
274                                                        );
275                                                                generated = Boolean.valueOf(source != null);
276                                                        }
277                                                }
278                                        } catch (Exception e) {
279                                                // ignore???
280                                        }
281                                }
282                        }
283                        generatedClassCache.put(clazz, generated);
284                }
285                
286                return generated.booleanValue();
287        }
288
289        @Override
290        public GroovyTemplateFactory getGroovyTemplateFactory() {
291                if (groovyTemplateFactory == null)
292                        groovyTemplateFactory = new GroovyTemplateFactory();
293                return groovyTemplateFactory;
294        }
295
296        public void resetClassLoader() {
297                generatedClassCache.clear();
298                dependentProjectConfigurations.clear();
299                loader = null;
300        }
301        
302        @Override
303        public ClassLoader getClassLoader() {
304                if (loader == null) {
305                        try {
306                                List<URL> classpath = new ArrayList<URL>();
307                                for (CpEntry entry : ProjectUtil.getFullResolvedClasspath(getJavaProject())) {
308                                        if (entry.getKind() == CpeKind.CONTAINER_JAR) {
309                                                for (CpEntry cEntry : entry.getChildren())
310                                                        addToClasspath(classpath, cEntry.toURL());
311                                        }
312                                        else
313                                                addToClasspath(classpath, entry.toURL());
314                                }
315                                
316                                for (Gas3Classpath gas3Classpath : getProperties().getGas3().getClasspaths()) {
317                                        File file = new File(gas3Classpath.getPath());
318                                        if (file.exists()) {
319                                                try {
320                                                        addToClasspath(classpath, file.toURI().toURL());
321                                                } catch (MalformedURLException e) {
322                                                        // Should never happen...
323                                                }
324                                        }
325                                }
326                                
327                                for (Gas3Project gas3Project : getProperties().getGas3().getProjects()) {
328                                        IProject dependentProject = ProjectUtil.getProject(getJavaProject().getProject(), gas3Project.getPath());
329                                        if (ProjectUtil.isGraniteProject(dependentProject)) {
330                                                for (CpEntry entry : ProjectUtil.getFullResolvedClasspath(JavaCore.create(dependentProject))) {
331                                                        if (entry.getKind() == CpeKind.CONTAINER_JAR) {
332                                                                for (CpEntry cEntry : entry.getChildren())
333                                                                        addToClasspath(classpath, cEntry.toURL());
334                                                        }
335                                                        else
336                                                                addToClasspath(classpath, entry.toURL());
337                                                }
338                                        }
339                                }
340                                
341                                if (getProperties().getGas3().isDebugEnabled()) {
342                                        listener.debug("Using classpath: {");
343                                        for (URL url : classpath)
344                                                listener.debug("  " + (url != null ? url.toString() : "<null>"));
345                                        listener.debug("}");
346                                }
347                                
348                                loader = URLClassLoader.newInstance(
349                                        classpath.toArray(new URL[classpath.size()]),
350                                        new BuilderParentClassLoader()
351                                );
352                        } catch (Exception e) {
353                                throw new RuntimeException(e);
354                        }
355                }
356                return loader;
357        }
358        
359        private void addToClasspath(List<URL> classpath, URL url) {
360                if (!classpath.contains(url))
361                        classpath.add(url);
362        }
363
364        @Override
365        public File getWorkingDirectory() {
366                return ProjectUtil.getProjectFile(project);
367        }
368}