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    
021    package org.granite.builder;
022    
023    import java.io.File;
024    import java.io.IOException;
025    import java.net.MalformedURLException;
026    import java.net.URL;
027    import java.net.URLClassLoader;
028    import java.util.ArrayList;
029    import java.util.Collections;
030    import java.util.HashMap;
031    import java.util.List;
032    import java.util.Map;
033    
034    import org.eclipse.core.resources.IProject;
035    import org.eclipse.core.runtime.CoreException;
036    import org.eclipse.jdt.core.IJavaProject;
037    import org.eclipse.jdt.core.JavaCore;
038    import org.granite.builder.properties.Gas3Classpath;
039    import org.granite.builder.properties.Gas3Project;
040    import org.granite.builder.properties.Gas3Source;
041    import org.granite.builder.properties.Gas3Translator;
042    import org.granite.builder.properties.GraniteProperties;
043    import org.granite.builder.properties.GranitePropertiesLoader;
044    import org.granite.builder.util.BuilderUtil;
045    import org.granite.builder.util.JavaClassInfo;
046    import org.granite.builder.util.ProjectUtil;
047    import org.granite.builder.util.ProjectUtil.CpEntry;
048    import org.granite.builder.util.ProjectUtil.CpEntry.CpeKind;
049    import org.granite.generator.Listener;
050    import org.granite.generator.TemplateUri;
051    import org.granite.generator.as3.As3TypeFactory;
052    import org.granite.generator.as3.DefaultAs3TypeFactory;
053    import org.granite.generator.as3.DefaultEntityFactory;
054    import org.granite.generator.as3.EntityFactory;
055    import org.granite.generator.as3.JavaAs3GroovyConfiguration;
056    import org.granite.generator.as3.JavaAs3Input;
057    import org.granite.generator.as3.PackageTranslator;
058    import org.granite.generator.as3.RemoteDestinationFactory;
059    import org.granite.generator.as3.reflect.JavaType.Kind;
060    import org.granite.generator.gsp.GroovyTemplateFactory;
061    
062    /**
063     * @author Franck WOLFF
064     */
065    public 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    }