Fixed: [  Issue 71  ] Changing project settings for proto doesn't trigger revalidation
https://code.google.com/p/protobuf-dt/issues/detail?id=71

Open editors are revalidated by default. User also is prompted to perform a full rebuild.
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/PreferenceAndPropertyPage.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/PreferenceAndPropertyPage.java
index e5966fd..c5b5c80 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/PreferenceAndPropertyPage.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/PreferenceAndPropertyPage.java
@@ -219,9 +219,13 @@
 
   @Override public final boolean performOk() {
     preferenceBinder.saveValues();
+    okPerformed();
     return true;
   }
 
+  /** Method invoked after this page's values have been saved. By default this method does nothing. */
+  protected void okPerformed() {}
+
   @Override protected final void performDefaults() {
     preferenceBinder.applyDefaults();
     updateContents();
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/compiler/CodeGenerationEditor.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/compiler/CodeGenerationEditor.java
index 0449722..5ac4f8d 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/compiler/CodeGenerationEditor.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/compiler/CodeGenerationEditor.java
@@ -9,7 +9,6 @@
 package com.google.eclipse.protobuf.ui.preferences.pages.compiler;
 
 import static com.google.eclipse.protobuf.ui.preferences.pages.compiler.Messages.*;
-import static java.util.Collections.unmodifiableList;
 import static org.eclipse.jface.window.Window.OK;
 
 import java.util.*;
@@ -118,13 +117,9 @@
     });
   }
 
-  public List<CodeGenerationSetting> codeGenerationOptions() {
-    return unmodifiableList(settings);
-  }
-
-  public void codeGenerationSettings(List<CodeGenerationSetting> newSettings) {
+  public void codeGenerationSettings(CodeGenerationSettings newSettings) {
     settings.clear();
-    settings.addAll(newSettings);
+    settings.addAll(newSettings.allSettings());
     updateTable();
   }
 
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/compiler/CompilerPreferencePage.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/compiler/CompilerPreferencePage.java
index 50b0976..797f57c 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/compiler/CompilerPreferencePage.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/compiler/CompilerPreferencePage.java
@@ -169,7 +169,7 @@
 
   private void checkState() {
     boolean atLeastOneEnabled = false;
-    for (CodeGenerationSetting option : codeGenerationEditor.codeGenerationOptions()) {
+    for (CodeGenerationSetting option : codeGenerationSettings.allSettings()) {
       if (option.isEnabled()) {
         atLeastOneEnabled = true;
         break;
@@ -229,7 +229,7 @@
       shouldEnableCompilerOptions = shouldEnableCompilerOptions && useProjectSettings;
     }
     enableCompilerOptions(shouldEnableCompilerOptions);
-    codeGenerationEditor.codeGenerationSettings(codeGenerationSettings.allSettings());
+    codeGenerationEditor.codeGenerationSettings(codeGenerationSettings);
   }
 
   /** {@inheritDoc} */
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/paths/Messages.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/paths/Messages.java
index e24e4e9..a82b717 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/paths/Messages.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/paths/Messages.java
@@ -31,9 +31,11 @@
   public static String importedFilesPathResolution;
   public static String isWorkspacePathCheck;
   public static String pathResolution;
+  public static String rebuildProjectNow;
   public static String remove;
   public static String selectFileSystemDirectory;
   public static String selectWorkspaceDirectory;
+  public static String settingsChanged;
   public static String up;
 
   static {
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/paths/Messages.properties b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/paths/Messages.properties
index 450ed3d..601cdf8 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/paths/Messages.properties
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/paths/Messages.properties
@@ -14,7 +14,9 @@
 importedFilesPathResolution=Path resolution of imported files
 isWorkspacePathCheck=Is a workspace path
 pathResolution=Path Resolution
+rebuildProjectNow=By default, all open editors will be revalidated. In addition, a full rebuild may be necessary for changes to take effect on the whole project. Do the full build now?
 remove=&Remove
 selectFileSystemDirectory=Select a directory from file system:
 selectWorkspaceDirectory=Select a directory from workspace:
+settingsChanged=Import Paths Settings Changed
 up=Move Up
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/paths/PathsPreferencePage.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/paths/PathsPreferencePage.java
index 5d96567..30b919f 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/paths/PathsPreferencePage.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/pages/paths/PathsPreferencePage.java
@@ -13,11 +13,17 @@
 import static com.google.eclipse.protobuf.ui.swt.EventListeners.addSelectionListener;
 import static java.util.Arrays.asList;
 import static java.util.Collections.unmodifiableList;
+import static org.eclipse.core.resources.IncrementalProjectBuilder.FULL_BUILD;
+import static org.eclipse.core.runtime.Status.OK_STATUS;
+import static org.eclipse.core.runtime.jobs.Job.BUILD;
 import static org.eclipse.xtext.util.Strings.*;
 
 import java.util.*;
 import java.util.List;
 
+import org.apache.log4j.Logger;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.*;
@@ -28,6 +34,7 @@
 import com.google.eclipse.protobuf.ui.preferences.*;
 import com.google.eclipse.protobuf.ui.preferences.binding.*;
 import com.google.eclipse.protobuf.ui.preferences.pages.PreferenceAndPropertyPage;
+import com.google.eclipse.protobuf.ui.validation.ValidationTrigger;
 import com.google.inject.Inject;
 
 /**
@@ -37,7 +44,9 @@
  */
 public class PathsPreferencePage extends PreferenceAndPropertyPage {
 
-  private static final String COMMA_DELIMITER = ",";
+  private static Logger logger = Logger.getLogger(PathsPreferencePage.class);
+
+  private static final String COMMA_DELIMITER = ","; //$NON-NLS-1$
   private static final String PREFERENCE_PAGE_ID = PathsPreferencePage.class.getName();
 
   private Group grpResolutionOfImported;
@@ -46,6 +55,9 @@
   private DirectoryPathsEditor directoryPathsEditor;
 
   @Inject private PluginImageHelper imageHelper;
+  @Inject private ValidationTrigger validation;
+
+  private boolean stateChanged;
 
   @Override protected Composite contentParent(Composite parent) {
     Composite contents = new Composite(parent, SWT.NONE);
@@ -91,6 +103,7 @@
   }
 
   private void checkState() {
+    stateChanged = true;
     if (directoryPathsEditor.isEnabled() && directoryPathsEditor.directoryPaths().isEmpty()) {
       pageIsNowInvalid(errorNoDirectoryNames);
       return;
@@ -104,10 +117,8 @@
 
   @Override protected void setupBinding(PreferenceBinder preferenceBinder) {
     RawPreferences preferences = new RawPreferences(getPreferenceStore());
-    preferenceBinder.addAll(
-        bindSelectionOf(btnOneDirectoryOnly).to(preferences.filesInOneDirectoryOnly()),
-        bindSelectionOf(btnMultipleDirectories).to(preferences.filesInMultipleDirectories())
-      );
+    preferenceBinder.addAll(bindSelectionOf(btnOneDirectoryOnly).to(preferences.filesInOneDirectoryOnly()),
+        bindSelectionOf(btnMultipleDirectories).to(preferences.filesInMultipleDirectories()));
     final StringPreference directoryPaths = preferences.directoryPaths();
     preferenceBinder.add(new Binding() {
       public void applyPreferenceValueToTarget() {
@@ -126,7 +137,7 @@
 
   private String directoryNames() {
     List<DirectoryPath> paths = directoryPathsEditor.directoryPaths();
-    if (paths.isEmpty()) return "";
+    if (paths.isEmpty()) return ""; //$NON-NLS-1$
     List<String> pathsAsText = new ArrayList<String>();
     for (DirectoryPath path : paths) {
       pathsAsText.add(path.toString());
@@ -159,8 +170,41 @@
     directoryPathsEditor.setEnabled(btnMultipleDirectories.getSelection() && enabled);
   }
 
-  /** {@inheritDoc} */
   @Override protected String preferencePageId() {
     return PREFERENCE_PAGE_ID;
   }
+
+  @Override protected void okPerformed() {
+    // TODO check threading
+    if (!stateChanged) return;
+    stateChanged = false;
+    if (shouldRebuild()) {
+      rebuildProject();
+      return;
+    }
+    validation.validateOpenEditors(project());
+  }
+
+  private boolean shouldRebuild() {
+    MessageBox messageBox = new MessageBox(getShell(), SWT.ICON_QUESTION | SWT.YES | SWT.NO);
+    messageBox.setText(settingsChanged);
+    messageBox.setMessage(rebuildProjectNow);
+    return messageBox.open() == SWT.YES;
+  }
+
+  private void rebuildProject() {
+    Job job = new Job("Rebuilding project") { //$NON-NLS-1$
+      protected IStatus run(IProgressMonitor monitor) {
+        try {
+          project().build(FULL_BUILD, monitor);
+        } catch (CoreException e) {
+          logger.error(e.getMessage(), e);
+          return new Status(ERROR, "unknown", ERROR, e.getMessage(), e); //$NON-NLS-1$
+        }
+        return OK_STATUS;
+      }
+    };
+    job.setPriority(BUILD);
+    job.schedule(); // start as soon as possible
+  }
 }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Resources.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Resources.java
index bfa04d0..b4ca2e7 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Resources.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Resources.java
@@ -106,6 +106,23 @@
     return (path != null) ? root.getFile(path) : null;
   }
 
+  /**
+   * Returns the project owning the file displayed in the given editor.
+   * @param editor the given editor.
+   * @return the project owning the file displayed in the given editor.
+   */
+  public IProject project(IEditorPart editor) {
+    IResource resource = resourceFrom(editor);
+    return (resource == null) ? null : resource.getProject();
+  }
+
+  private IResource resourceFrom(IEditorPart editor) {
+    if (editor == null) return null;
+    Object adapter = editor.getEditorInput().getAdapter(IResource.class);
+    return (adapter == null) ? null : (IResource) adapter;
+  }
+
+
   private IPath pathOf(URI uri) {
     String platformUri = uri.toPlatformString(true);
     return (platformUri != null) ? new Path(platformUri) : null;
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidateOnActivation.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidateOnActivation.java
index 3593840..f4df0b7 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidateOnActivation.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidateOnActivation.java
@@ -9,27 +9,13 @@
  */
 package com.google.eclipse.protobuf.ui.validation;
 
-import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.IMPORT__IMPORT_URI;
-import static org.eclipse.xtext.EcoreUtil2.getAllContentsOfType;
-
-import java.util.List;
+import static com.google.eclipse.protobuf.ui.validation.Validation.*;
 
 import org.eclipse.core.resources.IProject;
-import org.eclipse.emf.ecore.EObject;
 import org.eclipse.ui.*;
-import org.eclipse.ui.part.FileEditorInput;
-import org.eclipse.xtext.nodemodel.INode;
-import org.eclipse.xtext.resource.XtextResource;
-import org.eclipse.xtext.resource.impl.ListBasedDiagnosticConsumer;
-import org.eclipse.xtext.ui.editor.XtextEditor;
-import org.eclipse.xtext.ui.editor.model.*;
-import org.eclipse.xtext.util.concurrent.IUnitOfWork;
 
-import com.google.eclipse.protobuf.protobuf.Import;
-import com.google.eclipse.protobuf.ui.internal.ProtobufActivator;
 import com.google.eclipse.protobuf.ui.preferences.pages.general.*;
-import com.google.eclipse.protobuf.util.ModelNodes;
-import com.google.inject.Injector;
+import com.google.eclipse.protobuf.ui.util.Resources;
 
 /**
  * Validates a .proto file when it is opened or activated.
@@ -38,84 +24,73 @@
  */
 public class ValidateOnActivation implements IPartListener2 {
 
-  private static final String LANGUAGE_NAME = "com.google.eclipse.protobuf.Protobuf";
-
-  private final ModelNodes nodes = new ModelNodes();
-
+  /**
+   * Validates the active active editor in the given part that contains a .proto file in the Workspace.
+   * @param partRef the part that was activated.
+   */
   public void partActivated(IWorkbenchPartReference partRef) {
-    final XtextEditor editor = protoEditorFrom(partRef);
-    if (editor == null) return;
-    if (!shouldValidateEditor(editor.getResource().getProject())) return;
-    validate(editor);
+    IEditorPart activeEditor = activeEditor(partRef);
+    IProject project = projectFrom(activeEditor);
+    if (project == null || !shouldValidateEditor(project)) return;
+    validate(activeEditor);
+  }
+
+  private IEditorPart activeEditor(IWorkbenchPartReference partRef) {
+    IWorkbenchPage page = partRef.getPage();
+    return (page == null) ? null : page.getActiveEditor();
+  }
+
+  private IProject projectFrom(IEditorPart editor) {
+    Resources resources = injector().getInstance(Resources.class);
+    return resources.project(editor);
   }
 
   private boolean shouldValidateEditor(IProject project) {
-    Injector injector = ProtobufActivator.getInstance().getInjector(LANGUAGE_NAME);
-    GeneralPreferencesFactory factory = injector.getInstance(GeneralPreferencesFactory.class);
+    GeneralPreferencesFactory factory = injector().getInstance(GeneralPreferencesFactory.class);
     if (factory == null) return false;
     GeneralPreferences preferences = factory.preferences(project);
     return preferences.validateFilesOnActivation();
   }
 
-  private XtextEditor protoEditorFrom(IWorkbenchPartReference partRef) {
-    XtextEditor editor = xtextEditorFrom(partRef);
-    if (editor == null) return null;
-    if (!LANGUAGE_NAME.equals(editor.getLanguageName())) return null;
-    if (!(editor.getEditorInput() instanceof FileEditorInput)) return null;
-    return editor;
-  }
-
-  private XtextEditor xtextEditorFrom(IWorkbenchPartReference partRef) {
-    IWorkbenchPage page = partRef.getPage();
-    if (page == null) return null;
-    IEditorPart activeEditor = page.getActiveEditor();
-    return (activeEditor instanceof XtextEditor) ? (XtextEditor) activeEditor : null;
-  }
-
-  private void validate(XtextEditor editor) {
-    final IXtextDocument document = editor.getDocument();
-    if (!(document instanceof XtextDocument)) return;
-    document.readOnly(new IUnitOfWork<Void, XtextResource>() {
-      public java.lang.Void exec(XtextResource resource) throws Exception {
-        EObject root = resource.getParseResult().getRootASTElement();
-        resetImports(root);
-        resource.getLinker().linkModel(root, new ListBasedDiagnosticConsumer());
-        ((XtextDocument) document).checkAndUpdateAnnotations();
-        return null;
-      }
-    });
-  }
-
-  private void resetImports(EObject root) {
-    List<Import> imports = getAllContentsOfType(root, Import.class);
-    for (Import anImport : imports) resetUri(anImport);
-  }
-
-  private void resetUri(Import anImport) {
-    String uri = uriAsEnteredInEditor(anImport);
-    if (uri == null) return;
-    anImport.setImportURI(uri);
-  }
-
-  private String uriAsEnteredInEditor(Import anImport) {
-    INode node = nodes.firstNodeForFeature(anImport, IMPORT__IMPORT_URI);
-    if (node == null) return null;
-    String text = node.getText();
-    if (text == null) return null;
-    return text.substring(1, text.length() - 1);
-  }
-
+  /**
+   * This method does nothing.
+   * @param partRef the part that was surfaced.
+   */
   public void partBroughtToTop(IWorkbenchPartReference partRef) {}
 
+  /**
+   * This method does nothing.
+   * @param partRef the part that was closed.
+   */
   public void partClosed(IWorkbenchPartReference partRef) {}
 
+  /**
+   * This method does nothing.
+   * @param partRef the part that was deactivated.
+   */
   public void partDeactivated(IWorkbenchPartReference partRef) {}
 
+  /**
+   * This method does nothing.
+   * @param partRef the part that was opened.
+   */
   public void partOpened(IWorkbenchPartReference partRef) {}
 
+  /**
+   * This method does nothing.
+   * @param partRef the part that was hidden.
+   */
   public void partHidden(IWorkbenchPartReference partRef) {}
 
+  /**
+   * This method does nothing.
+   * @param partRef the part that is visible.
+   */
   public void partVisible(IWorkbenchPartReference partRef) {}
 
+  /**
+   * This method does nothing.
+   * @param partRef the part whose input was changed.
+   */
   public void partInputChanged(IWorkbenchPartReference partRef) {}
 }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/Validation.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/Validation.java
new file mode 100644
index 0000000..a5b1205
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/Validation.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.protobuf.ui.validation;
+
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.IMPORT__IMPORT_URI;
+import static org.eclipse.xtext.EcoreUtil2.getAllContentsOfType;
+
+import java.util.List;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.xtext.nodemodel.INode;
+import org.eclipse.xtext.resource.XtextResource;
+import org.eclipse.xtext.resource.impl.ListBasedDiagnosticConsumer;
+import org.eclipse.xtext.ui.editor.XtextEditor;
+import org.eclipse.xtext.ui.editor.model.*;
+import org.eclipse.xtext.util.concurrent.IUnitOfWork;
+
+import com.google.eclipse.protobuf.protobuf.Import;
+import com.google.eclipse.protobuf.ui.internal.ProtobufActivator;
+import com.google.eclipse.protobuf.util.ModelNodes;
+import com.google.inject.Injector;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+final class Validation {
+
+  private static final String LANGUAGE_NAME = "com.google.eclipse.protobuf.Protobuf";
+
+  static void validate(IEditorPart editor) {
+    XtextEditor protoEditor = asProtoEditor(editor);
+    if (protoEditor == null) return;
+    validate(protoEditor);
+  }
+
+  private static XtextEditor asProtoEditor(IEditorPart editor) {
+    XtextEditor xtextEditor = asXtextEditor(editor);
+    if (xtextEditor == null) return null;
+    if (!LANGUAGE_NAME.equals(xtextEditor.getLanguageName())) return null;
+    return xtextEditor;
+  }
+
+  private static XtextEditor asXtextEditor(IEditorPart editor) {
+    if (!isXtextEditorContainingWorkspaceFile(editor)) return null;
+    return (XtextEditor) editor;
+  }
+
+  private static boolean isXtextEditorContainingWorkspaceFile(IEditorPart editor) {
+    return editor instanceof XtextEditor && editor.getEditorInput() instanceof FileEditorInput;
+  }
+
+  private static void validate(XtextEditor editor) {
+    final IXtextDocument document = editor.getDocument();
+    if (!(document instanceof XtextDocument)) return;
+    document.readOnly(new IUnitOfWork<Void, XtextResource>() {
+      public java.lang.Void exec(XtextResource resource) throws Exception {
+        EObject root = resource.getParseResult().getRootASTElement();
+        resetImports(root);
+        resource.getLinker().linkModel(root, new ListBasedDiagnosticConsumer());
+        ((XtextDocument) document).checkAndUpdateAnnotations();
+        return null;
+      }
+    });
+  }
+
+  private static void resetImports(EObject root) {
+    List<Import> imports = getAllContentsOfType(root, Import.class);
+    for (Import anImport : imports) resetUri(anImport);
+  }
+
+  private static void resetUri(Import anImport) {
+    String uri = uriAsEnteredInEditor(anImport);
+    if (uri == null) return;
+    anImport.setImportURI(uri);
+  }
+
+  private static String uriAsEnteredInEditor(Import anImport) {
+    INode node = nodes().firstNodeForFeature(anImport, IMPORT__IMPORT_URI);
+    if (node == null) return null;
+    String text = node.getText();
+    if (text == null) return null;
+    return text.substring(1, text.length() - 1);
+  }
+
+  private static ModelNodes nodes() {
+    return injector().getInstance(ModelNodes.class);
+  }
+
+  static Injector injector() {
+    return ProtobufActivator.getInstance().getInjector(LANGUAGE_NAME);
+  }
+
+  private Validation() {}
+}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidationTrigger.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidationTrigger.java
new file mode 100644
index 0000000..9fc9f98
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidationTrigger.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * 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
+ */
+package com.google.eclipse.protobuf.ui.validation;
+
+import static com.google.eclipse.protobuf.ui.validation.Validation.validate;
+
+import java.net.URI;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.ui.*;
+
+import com.google.eclipse.protobuf.ui.util.Resources;
+import com.google.inject.*;
+
+/**
+ * Triggers validation of .proto files.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@Singleton
+public class ValidationTrigger {
+
+  private final String PROTO_EDITOR_ID = "com.google.eclipse.protobuf.Protobuf";
+
+  @Inject private Resources resources;
+
+  /**
+   * Triggers validation of all open .proto files belonging to the given project.
+   * @param project the given project.
+   */
+  public void validateOpenEditors(IProject project) {
+    for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) {
+      for (IWorkbenchPage page : window.getPages()) {
+        for (IEditorReference editorRef : page.getEditorReferences()) {
+          validateFileInEditor(editorRef, project);
+        }
+      }
+    }
+  }
+
+  private void validateFileInEditor(IEditorReference editorRef, IProject project) {
+    if (!PROTO_EDITOR_ID.equals(editorRef.getId())) return;
+    IEditorPart editor = editorRef.getEditor(true);
+    IProject fileProject = resources.project(editor);
+    if (fileProject == null || !haveEqualUris(project, fileProject)) return;
+    validate(editor);
+  }
+
+  private boolean haveEqualUris(IProject p1, IProject p2) {
+    if (p1 == null || p2 == null) return false;
+    URI uri1 = p1.getLocationURI();
+    URI uri2 = p2.getLocationURI();
+    return areEqual(uri1, uri2);
+  }
+
+  private boolean areEqual(URI uri1, URI uri2) {
+    if (uri1 == null) return false;
+    return uri1.equals(uri2);
+  }
+}