/**
* Felinx - Integration link between Felix and Eclipse
Copyright (C) 2013 Michiel Vermandel
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jerry.felinx.plugin.builder;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.URI;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.jerry.felinx.plugin.Activator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
public class BundleBuilder extends IncrementalProjectBuilder {
class SampleDeltaVisitor implements IResourceDeltaVisitor {
/*
* (non-Javadoc)
*
* @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
*/
public boolean visit(IResourceDelta delta) throws CoreException {
IResource resource = delta.getResource();
//System.out.println("SampleDeltaVisitor: " + delta.getKind() + " "+ resource.getFullPath());
switch (delta.getKind()) {
case IResourceDelta.ADDED:
// handle added resource
checkXML(resource);
break;
case IResourceDelta.REMOVED:
// handle removed resource
break;
case IResourceDelta.CHANGED:
// handle changed resource
checkXML(resource);
break;
}
// return true to continue visiting children.
return true;
}
}
class SampleResourceVisitor implements IResourceVisitor {
public boolean visit(IResource resource) {
//System.out.println("SampleResourceVisitor: " + resource.getFullPath());
checkXML(resource);
// return true to continue visiting children.
return true;
}
}
class XMLErrorHandler extends DefaultHandler {
private IFile file;
public XMLErrorHandler(IFile file) {
this.file = file;
}
private void addMarker(SAXParseException e, int severity) {
BundleBuilder.this.addMarker(file, e.getMessage(), e.getLineNumber(), severity);
}
public void error(SAXParseException exception) throws SAXException {
addMarker(exception, IMarker.SEVERITY_ERROR);
}
public void fatalError(SAXParseException exception) throws SAXException {
addMarker(exception, IMarker.SEVERITY_ERROR);
}
public void warning(SAXParseException exception) throws SAXException {
addMarker(exception, IMarker.SEVERITY_WARNING);
}
}
public static final String BUILDER_ID = "FelinxPlugin.BundleBuilder";
private static final String MARKER_TYPE = "FelinxPlugin.xmlProblem";
private SAXParserFactory parserFactory;
private void addMarker(IFile file, String message, int lineNumber, int severity) {
try {
IMarker marker = file.createMarker(MARKER_TYPE);
marker.setAttribute(IMarker.MESSAGE, message);
marker.setAttribute(IMarker.SEVERITY, severity);
if (lineNumber == -1) {
lineNumber = 1;
}
marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
} catch (CoreException e) {
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.internal.events.InternalBuilder#build(int, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
*/
protected IProject[] build(int kind, @SuppressWarnings("rawtypes") Map args, IProgressMonitor monitor) throws CoreException {
if (kind == FULL_BUILD || kind == CLEAN_BUILD) {
fullBuild(monitor);
} else {
IResourceDelta delta = getDelta(getProject());
if (delta == null) {
fullBuild(monitor);
} else {
incrementalBuild(delta, monitor);
}
}
return null;
}
void checkXML(IResource resource) {
if (resource instanceof IFile && resource.getName().endsWith(".xml")) {
IFile file = (IFile) resource;
deleteMarkers(file);
XMLErrorHandler reporter = new XMLErrorHandler(file);
try {
getParser().parse(file.getContents(), reporter);
} catch (Exception e1) {
}
}
}
private void deleteMarkers(IFile file) {
try {
file.deleteMarkers(MARKER_TYPE, false, IResource.DEPTH_ZERO);
} catch (CoreException ce) {
}
}
protected void fullBuild(final IProgressMonitor monitor) throws CoreException {
try {
System.out.println("fullBuild");
getProject().accept(new SampleResourceVisitor());
createBundle(getProject(), monitor);
} catch (CoreException e) {
}
}
private SAXParser getParser() throws ParserConfigurationException, SAXException {
if (parserFactory == null) {
parserFactory = SAXParserFactory.newInstance();
}
return parserFactory.newSAXParser();
}
protected void incrementalBuild(IResourceDelta delta, IProgressMonitor monitor) throws CoreException {
System.out.println("incrementalBuild");
// the visitor does the work.
delta.accept(new SampleDeltaVisitor());
createBundle(getProject(), monitor);
}
public IPath getJarPath(IProject project) throws CoreException {
IPath location = null;
final IFolder generated = project.getFolder(new Path("generated"));
if (!generated.exists()) {
generated.create(true, true, null);
}
location = generated.getFile(project.getName() + ".jar").getLocation();
return location;
}
public ActionResult createBundle(IProject project, IProgressMonitor monitor) {
ActionResult result = new ActionResult("Creating bundle...");
try {
IJavaProject javaProject = JavaCore.create(project);
final IFolder generated = javaProject.getProject().getFolder(new Path("generated"));
if (!generated.exists()) {
generated.create(true, true, monitor);
}
IPath location = getJarPath(project);
IFolder metaFolder = javaProject.getProject().getFolder("META-INF");
IFile file = metaFolder.getFile("MANIFEST.MF");
Manifest manifest = new Manifest(file.getContents());
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
JarOutputStream target = new JarOutputStream(new FileOutputStream(location.toOSString()), manifest);
// add(new File("inputDirectory"), target);
// // http://stackoverflow.com/questions/3273022/getting-all-classes-from-the-current-workspace-in-eclipse
// System.out.println("Class files:");
// for (IPackageFragment packageFragment : javaProject.getPackageFragments()) {
// // Package fragments include all packages in the class path. We will only look at the package from the source folder.
// // K_BINARY would include also included JARS, e.g. rt.jar
// if (packageFragment.getKind() == IPackageFragmentRoot.K_SOURCE) {
// System.out.println("Handling packageFragment " + packageFragment.getElementName() +" "+ packageFragment.getPath() + " ");
// for (final IJavaElement element : packageFragment.getChildren()) {
// System.out.println(">> "+element.getPath());
// }
// }
// }
System.out.println("Adding classes...");
File projectRoot = project.getLocation().toFile();
File classesFolder = getClassesFolder(javaProject);
//do not add bin or classes folder, but directly its content
addFiles(classesFolder, classesFolder, target);
final IFolder lib = javaProject.getProject().getFolder(new Path("lib"));
if (lib.exists()) {
monitor.setTaskName("Adding libraries...");
addFiles(new File(lib.getLocationURI()), projectRoot, target);
}
target.close();
File jar = new File(location.toOSString());
System.out.println("Performing iPOJO manipulation...");
IPojoManipulator man = new IPojoManipulator();
man.setInput(jar);
System.out.println("Executing...");
man.execute();
System.out.println("Installing... ");
String symbolicName=null;
String bundleVersion=null;
Map<Object, Object> bundleSymbolicNameAtrrs = manifest.getMainAttributes();
for( Object obj : bundleSymbolicNameAtrrs.keySet()){
if (obj.toString().equals("Bundle-SymbolicName")){
symbolicName = bundleSymbolicNameAtrrs.get(obj).toString();
}else if (obj.toString().equals("Bundle-Version")){
bundleVersion = bundleSymbolicNameAtrrs.get(obj).toString();
}
if (symbolicName!=null && bundleVersion!=null){
break;
}
}
Activator.updateBundle(symbolicName, bundleVersion, jar);
} catch (Throwable e) {
System.err.println(e.getMessage());
e.printStackTrace();
result.setError(e);
}
return result;
}
private File getClassesFolder(IJavaProject javaProject) throws JavaModelException {
IPath classesFolder = javaProject.getOutputLocation();
return new File(getProjectRoot(javaProject).getParent(), classesFolder.toOSString());
}
private File getProjectRoot(IJavaProject javaProject) {
return javaProject.getProject().getLocation().toFile();
}
private void addFiles(File fullPath, File projectRoot, JarOutputStream target) throws Exception {
BufferedInputStream in = null;
try {
File relative = getRelativeFile(projectRoot, fullPath);
if (fullPath.isDirectory()) {
String name = relative.getPath().replace("\\", "/");
if (!name.isEmpty()) {
if (!name.endsWith("/"))
name += "/";
JarEntry entry = new JarEntry(name);
entry.setTime(fullPath.lastModified());
target.putNextEntry(entry);
target.closeEntry();
}
for (File nestedFile : fullPath.listFiles())
addFiles(nestedFile, projectRoot, target);
return;
}
//System.out.println("Writing " + fullPath.getAbsolutePath());
JarEntry entry = new JarEntry(relative.getPath().replace("\\", "/"));
entry.setTime(fullPath.lastModified());
target.putNextEntry(entry);
in = new BufferedInputStream(new FileInputStream(fullPath));
byte[] buffer = new byte[1024];
while (true) {
int count = in.read(buffer);
if (count == -1)
break;
target.write(buffer, 0, count);
}
target.closeEntry();
} finally {
if (in != null)
in.close();
}
}
private File getRelativeFile(File base, File childOfBase) {
URI relative = base.toURI().relativize(childOfBase.toURI());
return new File(relative.getPath());
}
}