blob: 9a14c5b9b7fab6f2ad2e7ea269569e8ecafa3676 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.java.preprocessorbridge.spi;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.event.ChangeListener;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.java.queries.BinaryForSourceQuery;
import org.openide.filesystems.FileUtil;
import org.openide.util.BaseUtilities;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.Parameters;
/**
* The Compile On Save performer.
* @since 1.41
* @author Tomas Zezula
*/
public interface CompileOnSaveAction {
/**
* Performs the Compile On Save operation.
* @param ctx the context for Compile On Save operation
* @return true in case of success, false in case of failure, null in case of no changes.
* @throws IOException
*/
Boolean performAction (@NonNull final Context ctx) throws IOException;
/**
* Returns true when this action is enabled.
* The first enabled {@link CompileOnSaveAction} is used for performing the Compile On Save operation.
* @return true when enabled
*/
boolean isEnabled();
/**
* Returns true when resources should be synchronized.
* @return true when resources should be copied
*/
boolean isUpdateResources();
/**
* Returns true when classes should be synchronized.
* @return true when classes should be copied
*/
boolean isUpdateClasses();
/**
* Adds {@link ChangeListener}.
* @param l the listener to be added
*/
void addChangeListener(@NonNull ChangeListener l);
/**
* Removes {@link ChangeListener}.
* @param l the listener to be removed
*/
void removeChangeListener(@NonNull ChangeListener l);
/**
* Compile On Save operation.
*/
enum Operation {
/**
* Clean.
*/
CLEAN,
/**
* Partial update.
*/
UPDATE,
/**
* Full synchronization.
*/
SYNC
}
/**
* Context of the Compile On Save Operation.
*/
final class Context {
private final Operation operation;
private final URL srcRoot;
private final boolean isCopyResources;
private final boolean isKeepResourcesUpToDate;
private final boolean isAllFilesIndexing;
private final File cacheRoot;
private final Iterable<? extends File> updated;
private final Iterable<? extends File> deleted;
private final Object owner;
private final Consumer<Iterable<File>> firer;
private Context(
@NonNull final Operation operation,
@NonNull final URL srcRoot,
final boolean isCopyResources,
final boolean isKeepResourcesUpToDate,
final boolean isAllFilesIndexing,
@NullAllowed final File cacheRoot,
@NullAllowed final Iterable<? extends File> updated,
@NullAllowed final Iterable<? extends File> deleted,
@NullAllowed final Object owner,
@NullAllowed final Consumer<Iterable<File>> firer) {
this.operation = operation;
this.srcRoot = srcRoot;
this.isCopyResources = isCopyResources;
this.isKeepResourcesUpToDate = isKeepResourcesUpToDate;
this.isAllFilesIndexing = isAllFilesIndexing;
this.cacheRoot = cacheRoot;
this.updated = updated;
this.deleted = deleted;
this.owner = owner;
this.firer = firer;
}
/**
* Returns the kind of the Compile On Save operation.
* @return the {@link Operation}
*/
@NonNull
public Operation getOperation() {
return operation;
}
/**
* Returns the changed files.
* The operation is valid only for {@link Operation#UPDATE}.
* @return the changed files
*/
@NonNull
public Iterable<? extends File> getUpdated() {
if (operation != Operation.UPDATE) {
throw new IllegalStateException();
}
return updated;
}
/**
* Returns the deleted files.
* The operation is valid only for {@link Operation#UPDATE}.
* @return the deleted files
*/
@NonNull
public Iterable<? extends File> getDeleted() {
if (operation != Operation.UPDATE) {
throw new IllegalStateException();
}
return deleted;
}
/**
* Returns true for resources.
* The operation is valid only for {@link Operation#UPDATE} and {@link Operation#SYNC}.
* @return true for resources
*/
public boolean isCopyResources() {
if (operation == Operation.CLEAN) {
throw new IllegalStateException();
}
return isCopyResources;
}
/**
* Returns true if resources should be updated on change.
* The operation is valid only for {@link Operation#SYNC}.
* @return true for update on change
*/
public boolean isKeepResourcesUpToDate() {
if (operation != Operation.SYNC) {
throw new IllegalStateException();
}
return isKeepResourcesUpToDate;
}
/**
* Returns the source root.
* @return the source root
*/
@NonNull
public URL getSourceRoot() {
return srcRoot;
}
/**
* Returns the cache root.
* The operation is valid only for {@link Operation#UPDATE}.
* @return the cache root.
*/
@NonNull
public File getCacheRoot() {
if (operation != Operation.UPDATE) {
throw new IllegalStateException();
}
return cacheRoot;
}
/**
* Returns the target folder.
* @return the target folder.
*/
@CheckForNull
public File getTarget() {
return getTarget(srcRoot);
}
/**
* Returns the target folder.
* @return the target folder.
*/
@CheckForNull
public URL getTargetURL() {
return getTargetURL(srcRoot);
}
/**
* Returns the root owner.
* The operation is valid only for {@link Operation#SYNC}.
* @return the owner
*/
@NonNull
public Object getOwner() {
if (operation != Operation.SYNC) {
throw new IllegalStateException();
}
return owner;
}
/**
* Fires updated files.
* @param updatedFiles the updated files
*/
public void filesUpdated(@NonNull final Iterable<File> updatedFiles) {
if (firer != null) {
firer.accept(updatedFiles);
}
}
/**
* Creates context for clean operation.
* @param srcRoot the root
* @return the {@link Context} for clean operation
*/
@NonNull
public static Context clean(@NonNull final URL srcRoot) {
Parameters.notNull("srcRoot", srcRoot); //NOI18N
return new Context(Operation.CLEAN, srcRoot, false, false, false, null, null, null, null, null);
}
/**
* Creates context for update operation.
* @param srcRoot the root
* @param isCopyResources true for resource update
* @param cacheRoot the cache root
* @param updated the changed files
* @param deleted the deleted files
* @param firer the fire callback
* @return the {@link Context} for update operation
*/
@NonNull
public static Context update(
@NonNull final URL srcRoot,
final boolean isCopyResources,
@NonNull final File cacheRoot,
@NonNull final Iterable<? extends File> updated,
@NonNull final Iterable<? extends File> deleted,
@NullAllowed final Consumer<Iterable<File>> firer) {
return update(srcRoot, isCopyResources, false, cacheRoot, updated, deleted, firer);
}
/**
* Creates context for update operation.
* @param srcRoot the root
* @param isCopyResources true for resource update
* @param isAllFilesIndexing true for all files indexing
* @param cacheRoot the cache root
* @param updated the changed files
* @param deleted the deleted files
* @param firer the fire callback
* @return the {@link Context} for update operation
* @since 1.50
*/
@NonNull
public static Context update(
@NonNull final URL srcRoot,
final boolean isCopyResources,
final boolean isAllFilesIndexing,
@NonNull final File cacheRoot,
@NonNull final Iterable<? extends File> updated,
@NonNull final Iterable<? extends File> deleted,
@NullAllowed final Consumer<Iterable<File>> firer) {
Parameters.notNull("srcRoot", srcRoot); //NOI18N
Parameters.notNull("cacheRoot", cacheRoot); //NOI18N
Parameters.notNull("updated", updated); //NOI18N
Parameters.notNull("deleted", deleted); //NOI18N
return new Context(
Operation.UPDATE, srcRoot, isCopyResources, false, isAllFilesIndexing, cacheRoot, updated, deleted, null, firer);
}
/**
* Creates context for sync operation.
* @param srcRoot the root
* @param isCopyResources should copy resources
* @param isKeepResourcesUpToDate should synchronize the resources on change
* @param owner the source root owner
* @return the {@link Context} for sync operation
*/
@NonNull
public static Context sync(
@NonNull final URL srcRoot,
final boolean isCopyResources,
final boolean isKeepResourcesUpToDate,
@NonNull final Object owner) {
Parameters.notNull("srcRoot", srcRoot); //NOI18N
Parameters.notNull("owner", owner); //NOI18N
return new Context(
Operation.SYNC, srcRoot, isCopyResources, isKeepResourcesUpToDate, false, null, null, null, owner, null);
}
/**
* Returns the target folder for source root.
* @param srcRoot the source root to return target folder for
* @return the target folder
*/
@CheckForNull
public static File getTarget(@NonNull URL srcRoot) {
BinaryForSourceQuery.Result binaryRoots = BinaryForSourceQuery.findBinaryRoots(srcRoot);
File result = null;
for (URL u : binaryRoots.getRoots()) {
assert u != null : "Null in BinaryForSourceQuery.Result.roots: " + binaryRoots; //NOI18N
if (u == null) {
continue;
}
File f = FileUtil.archiveOrDirForURL(u);
try {
if (FileUtil.isArchiveFile(BaseUtilities.toURI(f).toURL())) {
continue;
}
if (f != null && result != null) {
Logger.getLogger(CompileOnSaveAction.class.getName()).log(
Level.WARNING,
"More than one binary directory for root: {0}",
srcRoot.toExternalForm());
return null;
}
result = f;
} catch (MalformedURLException ex) {
Exceptions.printStackTrace(ex);
}
}
return result;
}
/**
* Returns the target folder for source root.
* @param srcRoot the source root to return target folder for
* @return the target folder
*/
@CheckForNull
public static URL getTargetURL(@NonNull URL srcRoot) {
BinaryForSourceQuery.Result binaryRoots = BinaryForSourceQuery.findBinaryRoots(srcRoot);
URL result = null;
for (URL u : binaryRoots.getRoots()) {
assert u != null : "Null in BinaryForSourceQuery.Result.roots: " + binaryRoots; //NOI18N
if (u == null || !"file".equals(u.getProtocol())) {
continue;
}
if (result != null) {
Logger.getLogger(CompileOnSaveAction.class.getName()).log(
Level.WARNING,
"More than one binary directory for root: {0}",
srcRoot.toExternalForm());
return null;
}
result = u;
}
return result;
}
/**
* Returns true if the action represents an all files indexing.
* @return true for all files indexing
* @since 1.50
*/
public boolean isAllFilesIndexing() {
return this.isAllFilesIndexing;
}
}
/**
* The provider of the {@link CompileOnSaveAction}.
* The instances of the {@link Provider} should be registered in the
* global {@link Lookup}.
*/
interface Provider {
/**
* Finds the Compile On Save performer for given source root.
* @param root the root to find the Compile On Save performer for.
* @return the {@link CompileOnSaveAction} or null when the root is not recognized.
*/
CompileOnSaveAction forRoot(@NonNull final URL root);
}
}