blob: 282b64ef482fc124bd7cfc849e74928fecebbcc4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 Pascal Essiembre.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pascal Essiembre - initial API and implementation
******************************************************************************/
package org.eclipse.babel.editor.builder;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.babel.core.message.IMessagesBundle;
import org.eclipse.babel.core.message.internal.MessagesBundle;
import org.eclipse.babel.core.message.internal.MessagesBundleGroup;
import org.eclipse.babel.core.message.manager.RBManager;
import org.eclipse.babel.editor.bundle.MessagesBundleGroupFactory;
import org.eclipse.babel.editor.plugin.MessagesEditorPlugin;
import org.eclipse.babel.editor.resource.validator.FileMarkerStrategy;
import org.eclipse.babel.editor.resource.validator.IValidationMarkerStrategy;
import org.eclipse.babel.editor.resource.validator.MessagesBundleGroupValidator;
import org.eclipse.babel.editor.util.UIUtils;
import org.eclipse.core.resources.IFile;
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.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
/**
* @author Pascal Essiembre
*
*/
public class Builder extends IncrementalProjectBuilder {
private static final Logger LOGGER = Logger.getLogger(Builder.class.getName());
public static final String BUILDER_ID = "org.eclipse.babel.editor.rbeBuilder"; //$NON-NLS-1$
private IValidationMarkerStrategy markerStrategy = new FileMarkerStrategy();
class SampleDeltaVisitor implements IResourceDeltaVisitor {
/**
* @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
*/
public boolean visit(IResourceDelta delta) throws CoreException {
IResource resource = delta.getResource();
// export file ending (.properties) into a configuration constant
if (resource instanceof IFile && resource.getName().endsWith(".properties")) {
switch (delta.getKind()) {
case IResourceDelta.ADDED:
// handle added resource
LOGGER.log(Level.INFO, "RBE DELTA added");
checkBundleResource(resource);
break;
case IResourceDelta.REMOVED:
LOGGER.log(Level.INFO, "RBE DELTA removed");
RBManager.getInstance(delta.getResource().getProject())
.notifyResourceRemoved(delta.getResource());
// handle removed resource
break;
case IResourceDelta.CHANGED:
// handle changed resource
LOGGER.log(Level.INFO, "RBE DELTA changed");
checkBundleResource(resource);
break;
}
}
// return true to continue visiting children.
return true;
}
}
class SampleResourceVisitor implements IResourceVisitor {
public boolean visit(IResource resource) {
checkBundleResource(resource);
// return true to continue visiting children.
return true;
}
}
/**
* list built during a single build of the properties files. Contains the
* list of files that must be validated. The validation is done only at the
* end of the visitor. This way the visitor can add extra files to be
* visited. For example: if the default properties file is changed, it is a
* good idea to rebuild all localized files in the same MessageBundleGroup
* even if themselves were not changed.
*/
private Set _resourcesToValidate;
/**
* Index built during a single build.
* <p>
* The values of that map are message bundles. The key is a resource that
* belongs to that message bundle.
* </p>
*/
private Map<IFile, MessagesBundleGroup> _alreadBuiltMessageBundle;
// /** one indexer per project we open and close it at the beginning and the
// end of each build. */
// private Indexer _indexer = new Indexer();
/**
* @see org.eclipse.core.resources.IncrementalProjectBuilder#build(int,
* java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
*/
protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
throws CoreException {
try {
_alreadBuiltMessageBundle = null;
_resourcesToValidate = null;
if (kind == FULL_BUILD) {
fullBuild(monitor);
} else {
IResourceDelta delta = getDelta(getProject());
if (delta == null) {
fullBuild(monitor);
} else {
incrementalBuild(delta, monitor);
}
}
} finally {
try {
finishBuild();
} catch (Exception e) {
e.printStackTrace();
} finally {
// must dispose the message bundles:
if (_alreadBuiltMessageBundle != null) {
for (MessagesBundleGroup msgGrp : _alreadBuiltMessageBundle
.values()) {
try {
// msgGrp.dispose(); // TODO: [alst] do we need this
// really?
} catch (Throwable t) {
// FIXME: remove this debugging:
System.err
.println("error disposing message-bundle-group "
+ msgGrp.getName());
// disregard crashes: we are doing our best effort
// to dispose things.
}
}
_alreadBuiltMessageBundle = null;
_resourcesToValidate = null;
}
// if (_indexer != null) {
// try {
// _indexer.close(true);
// _indexer.clear();
// } catch (CorruptIndexException e) {
// e.printStackTrace();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
}
}
return null;
}
/**
* Collect the resource bundles to validate and index the corresponding
* MessageBundleGroup(s).
*
* @param resource
* The resource currently validated.
*/
void checkBundleResource(IResource resource) {
if (true)
return; // TODO [alst]
if (resource instanceof IFile
&& resource.getName().endsWith(".properties")) { //$NON-NLS-1$ //TODO have customized?
IFile file = (IFile) resource;
if (file.isDerived()) {
return;
}
// System.err.println("Looking at " + file.getFullPath());
deleteMarkers(file);
MessagesBundleGroup msgBundleGrp = null;
if (_alreadBuiltMessageBundle == null) {
_alreadBuiltMessageBundle = new HashMap<IFile, MessagesBundleGroup>();
_resourcesToValidate = new HashSet();
} else {
msgBundleGrp = _alreadBuiltMessageBundle.get(file);
}
if (msgBundleGrp == null) {
msgBundleGrp = MessagesBundleGroupFactory.createBundleGroup(
null, file);
if (msgBundleGrp != null) {
// index the files for which this MessagesBundleGroup
// should be used for the validation.
// cheaper than creating a group for each on of those
// files.
boolean validateEntireGroup = false;
for (IMessagesBundle msgBundle : msgBundleGrp
.getMessagesBundles()) {
Object src = ((MessagesBundle) msgBundle).getResource()
.getSource();
// System.err.println(src + " -> " + msgBundleGrp);
if (src instanceof IFile) {// when it is a read-only
// thing we don't index it.
_alreadBuiltMessageBundle.put((IFile) src,
msgBundleGrp);
if (!validateEntireGroup && src == resource) {
if (((MessagesBundle) msgBundle).getLocale() == null
|| ((MessagesBundle) msgBundle)
.getLocale().equals(
UIUtils.ROOT_LOCALE)) {
// ok the default properties have been
// changed.
// make sure that all resources in this
// bundle group
// are validated too:
validateEntireGroup = true;
// TODO: eventually something similar.
// with foo_en.properties changed.
// then foo_en_US.properties must be
// revalidated
// and foo_en_CA.properties as well.
}
}
}
}
if (validateEntireGroup) {
for (IMessagesBundle msgBundle : msgBundleGrp
.getMessagesBundles()) {
Object src = ((MessagesBundle) msgBundle)
.getResource().getSource();
_resourcesToValidate.add(src);
}
}
}
}
_resourcesToValidate.add(resource);
}
}
/**
* Validates the message bundles collected by the visitor. Makes sure we
* validate only once each message bundle and build only once each
* MessageBundleGroup it belongs to.
*/
private void finishBuild() {
if (_resourcesToValidate != null) {
for (Iterator it = _resourcesToValidate.iterator(); it.hasNext();) {
IFile resource = (IFile) it.next();
MessagesBundleGroup msgBundleGrp = _alreadBuiltMessageBundle
.get(resource);
if (msgBundleGrp != null) {
// when null it is probably because it was skipped from
// the group because the locale was filtered.
try {
// System.out.println("Validate " + resource); //$NON-NLS-1$
// TODO check if there is a matching
// EclipsePropertiesEditorResource already open.
// else, create MessagesBundle from
// PropertiesIFileResource
MessagesBundle messagesBundle = msgBundleGrp
.getMessagesBundle(resource);
if (messagesBundle != null) {
Locale locale = messagesBundle.getLocale();
MessagesBundleGroupValidator.validate(msgBundleGrp,
locale, markerStrategy);
}// , _indexer);
} catch (Exception e) {
e.printStackTrace();
}
} else {
// System.out.println("Not validating " + resource); //$NON-NLS-1$
}
}
}
}
private void deleteMarkers(IFile file) {
try {
// System.out.println("Builder: deleteMarkers"); //$NON-NLS-1$
file.deleteMarkers(MessagesEditorPlugin.MARKER_TYPE, false,
IResource.DEPTH_ZERO);
} catch (CoreException ce) {
}
}
protected void fullBuild(final IProgressMonitor monitor)
throws CoreException {
// System.out.println("Builder: fullBuild"); //$NON-NLS-1$
getProject().accept(new SampleResourceVisitor());
}
protected void incrementalBuild(IResourceDelta delta,
IProgressMonitor monitor) throws CoreException {
// System.out.println("Builder: incrementalBuild"); //$NON-NLS-1$
delta.accept(new SampleDeltaVisitor());
}
protected void clean(IProgressMonitor monitor) throws CoreException {
ResourcesPlugin
.getWorkspace()
.getRoot()
.deleteMarkers(MessagesEditorPlugin.MARKER_TYPE, false,
IResource.DEPTH_INFINITE);
}
}