mirror of
https://github.com/maxkratz/moodle-sync-app.git
synced 2024-05-20 12:00:28 +00:00
Feature/remove dependencies (#24)
This commit is contained in:
parent
2294e48044
commit
2573ab0af9
|
@ -77,17 +77,6 @@
|
|||
<artifactId>commons-cli</artifactId>
|
||||
<version>1.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.lecturestudio.core</groupId>
|
||||
<artifactId>lect-core</artifactId>
|
||||
<version>4.0.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-client-microprofile</artifactId>
|
||||
|
|
|
@ -3,6 +3,9 @@ package moodle.sync.cli;
|
|||
import static java.util.Objects.nonNull;
|
||||
|
||||
import moodle.sync.cli.inject.ApplicationModule;
|
||||
import moodle.sync.core.app.dictionary.Dictionary;
|
||||
import moodle.sync.core.inject.GuiceInjector;
|
||||
import moodle.sync.core.inject.Injector;
|
||||
import moodle.sync.core.model.json.Course;
|
||||
import moodle.sync.core.model.json.MoodleUpload;
|
||||
import moodle.sync.core.model.json.Section;
|
||||
|
@ -11,10 +14,6 @@ import moodle.sync.core.web.service.MoodleUploadTemp;
|
|||
|
||||
import org.apache.commons.cli.*;
|
||||
|
||||
import org.lecturestudio.core.app.dictionary.Dictionary;
|
||||
import org.lecturestudio.core.inject.GuiceInjector;
|
||||
import org.lecturestudio.core.inject.Injector;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
|
|
|
@ -3,12 +3,12 @@ package moodle.sync.cli.inject;
|
|||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
|
||||
import moodle.sync.core.app.LocaleProvider;
|
||||
import moodle.sync.core.app.dictionary.Dictionary;
|
||||
import moodle.sync.core.beans.StringProperty;
|
||||
import moodle.sync.core.util.AggregateBundle;
|
||||
import moodle.sync.core.web.service.MoodleService;
|
||||
|
||||
import org.lecturestudio.core.app.LocaleProvider;
|
||||
import org.lecturestudio.core.app.dictionary.Dictionary;
|
||||
import org.lecturestudio.core.beans.StringProperty;
|
||||
import org.lecturestudio.core.util.AggregateBundle;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Locale;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package moodle.sync.cli.log;
|
||||
|
||||
import moodle.sync.core.app.AppDataLocator;
|
||||
import moodle.sync.core.log.Log4jXMLConfigurationFactory;
|
||||
import moodle.sync.core.model.VersionInfo;
|
||||
import org.apache.logging.log4j.core.config.Order;
|
||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
import org.lecturestudio.core.app.AppDataLocator;
|
||||
import org.lecturestudio.core.log.Log4jXMLConfigurationFactory;
|
||||
import org.lecturestudio.core.model.VersionInfo;
|
||||
|
||||
|
||||
@Plugin(name = "Log4jConfigurationFactory", category = "ConfigurationFactory")
|
||||
@Order(10)
|
||||
|
|
|
@ -50,17 +50,6 @@
|
|||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.19.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.lecturestudio.core</groupId>
|
||||
<artifactId>lect-core</artifactId>
|
||||
<version>4.0.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
|
@ -107,6 +96,36 @@
|
|||
<artifactId>commons-net</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-base</artifactId>
|
||||
<version>18.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-graphics</artifactId>
|
||||
<version>18.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-controls</artifactId>
|
||||
<version>18.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-fxml</artifactId>
|
||||
<version>18.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-artifact</artifactId>
|
||||
<version>3.8.5</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<parent>
|
||||
|
|
148
moodle-sync-core/src/main/java/moodle/sync/core/Executable.java
Normal file
148
moodle-sync-core/src/main/java/moodle/sync/core/Executable.java
Normal file
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core;
|
||||
|
||||
/**
|
||||
* Common interface to provide a consistent mechanism for executable components
|
||||
* managed by life cycle methods. The current state can be monitored by
|
||||
* observing the {@link ExecutableState}. The state will change to the error
|
||||
* state if the attempted transition is not valid.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public interface Executable {
|
||||
|
||||
/**
|
||||
* Prepare the executable component for starting. This method should perform
|
||||
* any initialization required post object creation.
|
||||
*
|
||||
* @throws ExecutableException if this component detects a fatal error that
|
||||
* prevents this component from being used.
|
||||
*/
|
||||
void init() throws ExecutableException;
|
||||
|
||||
/**
|
||||
* Prepare for the beginning of active use of this executable component.
|
||||
*
|
||||
* @throws ExecutableException if this component detects a fatal error that
|
||||
* prevents this component from being used.
|
||||
*/
|
||||
void start() throws ExecutableException;
|
||||
|
||||
/**
|
||||
* Stops this executable component.
|
||||
*
|
||||
* @throws ExecutableException if this component detects a fatal error that
|
||||
* needs to be reported.
|
||||
*/
|
||||
void stop() throws ExecutableException;
|
||||
|
||||
/**
|
||||
* Suspends this executable component.
|
||||
*
|
||||
* @throws ExecutableException if this component detects a fatal error that
|
||||
* needs to be reported.
|
||||
*/
|
||||
void suspend() throws ExecutableException;
|
||||
|
||||
/**
|
||||
* Dispose this executable component.
|
||||
*
|
||||
* @throws ExecutableException if this component detects a fatal error that
|
||||
* prevents this component from being destroyed.
|
||||
*/
|
||||
void destroy() throws ExecutableException;
|
||||
|
||||
/**
|
||||
* Obtain the current state of this executable component.
|
||||
*
|
||||
* @return The current state of this component.
|
||||
*/
|
||||
ExecutableState getState();
|
||||
|
||||
/**
|
||||
* Indicates whether this component has been created.
|
||||
*
|
||||
* @return {@code true} if this component has been created, otherwise {@code
|
||||
* false}.
|
||||
*/
|
||||
default boolean created() {
|
||||
return getState() == ExecutableState.Created;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this component has been initialized.
|
||||
*
|
||||
* @return {@code true} if this component has been initialized, otherwise
|
||||
* {@code false}.
|
||||
*/
|
||||
default boolean initialized() {
|
||||
return getState() == ExecutableState.Initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this component has been started.
|
||||
*
|
||||
* @return {@code true} if this component has been started, otherwise {@code
|
||||
* false}.
|
||||
*/
|
||||
default boolean started() {
|
||||
return getState() == ExecutableState.Started;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this component has been stopped.
|
||||
*
|
||||
* @return {@code true} if this component has been stopped, otherwise {@code
|
||||
* false}.
|
||||
*/
|
||||
default boolean stopped() {
|
||||
return getState() == ExecutableState.Stopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this component has been suspended.
|
||||
*
|
||||
* @return {@code true} if this component has been suspended, otherwise
|
||||
* {@code false}.
|
||||
*/
|
||||
default boolean suspended() {
|
||||
return getState() == ExecutableState.Suspended;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this component has been destroyed.
|
||||
*
|
||||
* @return {@code true} if this component has been destroyed, otherwise
|
||||
* {@code false}.
|
||||
*/
|
||||
default boolean destroyed() {
|
||||
return getState() == ExecutableState.Destroyed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether an error has occurred during a state transition of this
|
||||
* component.
|
||||
*
|
||||
* @return {@code true} if an error has occurred, otherwise {@code false}.
|
||||
*/
|
||||
default boolean error() {
|
||||
return getState() == ExecutableState.Error;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Subclasses may extend this executable base class by only implementing the
|
||||
* internal life cycle methods. This base implementation of the {@link
|
||||
* Executable} interface handles the proper state transition rules for the life
|
||||
* cycle methods.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public abstract class ExecutableBase implements Executable {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(ExecutableBase.class);
|
||||
|
||||
/** The list of registered state listeners for event notifications. */
|
||||
private final List<ExecutableStateListener> stateListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
/** The current state of this component. */
|
||||
private volatile ExecutableState state = ExecutableState.Created;
|
||||
|
||||
/** The previous state of this component. */
|
||||
private volatile ExecutableState prevState = ExecutableState.Created;
|
||||
|
||||
|
||||
/**
|
||||
* Adds an {@code ExecutableStateListener} to this component.
|
||||
*
|
||||
* @param listener The listener to add.
|
||||
*/
|
||||
public void addStateListener(ExecutableStateListener listener) {
|
||||
stateListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an {@code ExecutableStateListener} from this component.
|
||||
*
|
||||
* @param listener The listener to remove.
|
||||
*/
|
||||
public void removeStateListener(ExecutableStateListener listener) {
|
||||
stateListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final synchronized void init() throws ExecutableException {
|
||||
setState(ExecutableState.Initializing);
|
||||
|
||||
try {
|
||||
initInternal();
|
||||
}
|
||||
catch (ExecutableException e) {
|
||||
setState(ExecutableState.Error);
|
||||
|
||||
throw new ExecutableException("Failed to initialize Executable component [%s]", e, this);
|
||||
}
|
||||
|
||||
setState(ExecutableState.Initialized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ExecutableException if the subclass fails to initialize this
|
||||
* component.
|
||||
*/
|
||||
protected abstract void initInternal() throws ExecutableException;
|
||||
|
||||
@Override
|
||||
public final synchronized void start() throws ExecutableException {
|
||||
if (created() || destroyed()) {
|
||||
init();
|
||||
}
|
||||
|
||||
setState(ExecutableState.Starting);
|
||||
|
||||
try {
|
||||
startInternal();
|
||||
}
|
||||
catch (ExecutableException e) {
|
||||
setState(ExecutableState.Error);
|
||||
|
||||
throw new ExecutableException("Failed to start Executable component [%s]", e, this);
|
||||
}
|
||||
|
||||
setState(ExecutableState.Started);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ExecutableException if the subclass fails to start this
|
||||
* component.
|
||||
*/
|
||||
protected abstract void startInternal() throws ExecutableException;
|
||||
|
||||
@Override
|
||||
public final synchronized void stop() throws ExecutableException {
|
||||
setState(ExecutableState.Stopping);
|
||||
|
||||
try {
|
||||
stopInternal();
|
||||
}
|
||||
catch (ExecutableException e) {
|
||||
setState(ExecutableState.Error);
|
||||
|
||||
throw new ExecutableException("Failed to stop Executable component [%s]", e, this);
|
||||
}
|
||||
|
||||
setState(ExecutableState.Stopped);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ExecutableException if the sub-class fails to stop this component.
|
||||
*/
|
||||
protected abstract void stopInternal() throws ExecutableException;
|
||||
|
||||
@Override
|
||||
public final synchronized void suspend() throws ExecutableException {
|
||||
setState(ExecutableState.Suspending);
|
||||
|
||||
try {
|
||||
suspendInternal();
|
||||
}
|
||||
catch (ExecutableException e) {
|
||||
setState(ExecutableState.Error);
|
||||
|
||||
throw new ExecutableException("Failed to suspend Executable component [%s]", e, this);
|
||||
}
|
||||
|
||||
setState(ExecutableState.Suspended);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is meant to be overridden by sub-classes in order to implement
|
||||
* a custom suspend routine, if required.
|
||||
*
|
||||
* @throws ExecutableException if the sub-class fails to stop this component.
|
||||
*/
|
||||
protected void suspendInternal() throws ExecutableException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public final synchronized void destroy() throws ExecutableException {
|
||||
if (started() || suspended()) {
|
||||
stop();
|
||||
}
|
||||
|
||||
setState(ExecutableState.Destroying);
|
||||
|
||||
try {
|
||||
destroyInternal();
|
||||
}
|
||||
catch (ExecutableException e) {
|
||||
setState(ExecutableState.Error);
|
||||
|
||||
throw new ExecutableException("Failed to destroy Executable component [%s]", e, this);
|
||||
}
|
||||
|
||||
setState(ExecutableState.Destroyed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ExecutableException if the subclass fails to destroy this
|
||||
* component.
|
||||
*/
|
||||
protected abstract void destroyInternal() throws ExecutableException;
|
||||
|
||||
@Override
|
||||
public ExecutableState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the previous state of this component.
|
||||
*
|
||||
* @return The previous state of this component.
|
||||
*/
|
||||
public ExecutableState getPreviousState() {
|
||||
return prevState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the component state if, and only if, the attempted state transition
|
||||
* is valid.
|
||||
*
|
||||
* @param state The new state for this component.
|
||||
*
|
||||
* @exception ExecutableException if the state transition fails.
|
||||
*/
|
||||
protected final synchronized void setState(ExecutableState state) throws ExecutableException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Setting state for [{}] to [{}]", this, state);
|
||||
}
|
||||
|
||||
if (!validateNextState(state)) {
|
||||
throw new ExecutableException("Invalid state transition for Executable component [%s] in state [%s] to [%s]",
|
||||
this, getState(), state);
|
||||
}
|
||||
|
||||
this.prevState = this.state;
|
||||
this.state = state;
|
||||
|
||||
fireStateChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify state listeners about the new state. This method can be overridden
|
||||
* by subclasses in order to use a custom state notification.
|
||||
*/
|
||||
protected void fireStateChanged() {
|
||||
for (ExecutableStateListener listener : stateListeners) {
|
||||
listener.onExecutableStateChange(prevState, state);
|
||||
}
|
||||
}
|
||||
|
||||
final protected void logDebugMessage(String message, Object... messageParams) {
|
||||
LOG.debug(MessageFormat.format(message, messageParams));
|
||||
}
|
||||
|
||||
final protected void logTraceMessage(String message, Object... messageParams) {
|
||||
LOG.trace(MessageFormat.format(message, messageParams));
|
||||
}
|
||||
|
||||
final protected void logErrorMessage(String message, Object... messageParams) {
|
||||
LOG.error(MessageFormat.format(message, messageParams));
|
||||
}
|
||||
|
||||
final protected void logException(Throwable throwable, String throwMessage) {
|
||||
requireNonNull(throwable);
|
||||
requireNonNull(throwMessage);
|
||||
|
||||
LOG.error(throwMessage, throwable);
|
||||
}
|
||||
|
||||
private boolean validateNextState(ExecutableState nextState) {
|
||||
switch (this.state) {
|
||||
case Created:
|
||||
return isAllowed(nextState,
|
||||
ExecutableState.Initializing,
|
||||
ExecutableState.Destroying);
|
||||
|
||||
case Initializing:
|
||||
return isAllowed(nextState,
|
||||
ExecutableState.Initialized,
|
||||
ExecutableState.Error);
|
||||
|
||||
case Initialized:
|
||||
case Stopped:
|
||||
return isAllowed(nextState,
|
||||
ExecutableState.Starting,
|
||||
ExecutableState.Destroying);
|
||||
|
||||
case Starting:
|
||||
return isAllowed(nextState,
|
||||
ExecutableState.Started,
|
||||
ExecutableState.Error);
|
||||
|
||||
case Started:
|
||||
return isAllowed(nextState,
|
||||
ExecutableState.Suspending,
|
||||
ExecutableState.Stopping,
|
||||
ExecutableState.Destroying,
|
||||
ExecutableState.Error);
|
||||
|
||||
case Stopping:
|
||||
return isAllowed(nextState,
|
||||
ExecutableState.Stopped,
|
||||
ExecutableState.Error);
|
||||
|
||||
case Suspending:
|
||||
return isAllowed(nextState,
|
||||
ExecutableState.Suspended,
|
||||
ExecutableState.Error);
|
||||
|
||||
case Suspended:
|
||||
case Error: // Allow recovering from previous operation failure.
|
||||
return isAllowed(nextState,
|
||||
ExecutableState.Starting,
|
||||
ExecutableState.Stopping,
|
||||
ExecutableState.Destroying);
|
||||
|
||||
case Destroying:
|
||||
return isAllowed(nextState,
|
||||
ExecutableState.Destroyed,
|
||||
ExecutableState.Error);
|
||||
|
||||
case Destroyed:
|
||||
return isAllowed(nextState,
|
||||
ExecutableState.Initializing);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAllowed(ExecutableState nextState, ExecutableState... allowedStates) {
|
||||
if (isNull(allowedStates)) {
|
||||
throw new NullPointerException("No allowed states provided.");
|
||||
}
|
||||
|
||||
for (ExecutableState allowedState : allowedStates) {
|
||||
if (nextState == allowedState) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core;
|
||||
|
||||
/**
|
||||
* This exception is thrown to indicate a problem while operating an executable
|
||||
* component which implements the {@link Executable} interface. Throwing this
|
||||
* exception should be considered fatal to the operation of the application
|
||||
* containing this component.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class ExecutableException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 5074925163804690325L;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new ExecutableException with no other information.
|
||||
*/
|
||||
public ExecutableException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ExecutableException with the specified message.
|
||||
*
|
||||
* @param message A Message describing this exception.
|
||||
*/
|
||||
public ExecutableException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ExecutableException with the specified formatted
|
||||
* message.
|
||||
*
|
||||
* @param message A Message describing this exception.
|
||||
* @param args Arguments used in the formatted message.
|
||||
*/
|
||||
public ExecutableException(String message, Object... args) {
|
||||
super(String.format(message, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ExecutableException with the specified throwable.
|
||||
*
|
||||
* @param throwable A Throwable that caused this exception.
|
||||
*/
|
||||
public ExecutableException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ExecutableException with the specified message and
|
||||
* throwable.
|
||||
*
|
||||
* @param message A Message describing this exception.
|
||||
* @param throwable A Throwable that caused this exception.
|
||||
*/
|
||||
public ExecutableException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ExecutableException with the specified formatted message
|
||||
* and provided throwable.
|
||||
*
|
||||
* @param message A Message describing this exception.
|
||||
* @param throwable A Throwable that caused this exception.
|
||||
* @param args Arguments used in the formatted message.
|
||||
*/
|
||||
public ExecutableException(String message, Throwable throwable, Object... args) {
|
||||
super(String.format(message, args), throwable);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core;
|
||||
|
||||
/**
|
||||
* Valid states for executable components that implement the {@link Executable}
|
||||
* interface.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public enum ExecutableState {
|
||||
|
||||
/** The component has been created but not initialized yet. */
|
||||
Created,
|
||||
|
||||
/** The component is being initialized. */
|
||||
Initializing,
|
||||
|
||||
/** The component has been successfully initialized. */
|
||||
Initialized,
|
||||
|
||||
/** The component is being started. */
|
||||
Starting,
|
||||
|
||||
/** The component has been successfully started. */
|
||||
Started,
|
||||
|
||||
/** The component is being stopped. */
|
||||
Stopping,
|
||||
|
||||
/** The component has been successfully stopped. */
|
||||
Stopped,
|
||||
|
||||
/** The component is being suspended. */
|
||||
Suspending,
|
||||
|
||||
/** The component has been successfully suspended. */
|
||||
Suspended,
|
||||
|
||||
/** The component is being destroyed. */
|
||||
Destroying,
|
||||
|
||||
/** The component has been successfully destroyed. */
|
||||
Destroyed,
|
||||
|
||||
/** An fatal error has occurred during a state transition. */
|
||||
Error
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core;
|
||||
|
||||
/**
|
||||
* A listener for executable components implementing the {@link ExecutableBase}
|
||||
* class. The listener will be notified after a state transition has taken
|
||||
* place.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ExecutableStateListener {
|
||||
|
||||
/**
|
||||
* Receive the state transition event.
|
||||
*
|
||||
* @param oldState The previous state of the component.
|
||||
* @param newState The new state of the component.
|
||||
*/
|
||||
void onExecutableStateChange(ExecutableState oldState, ExecutableState newState);
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app;
|
||||
|
||||
|
||||
import moodle.sync.core.util.OsInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static java.util.Objects.nonNull;
|
||||
|
||||
/**
|
||||
* The application data locator translates the paths of application related
|
||||
* files to the corresponding application data folder.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class AppDataLocator {
|
||||
|
||||
/** The application name. */
|
||||
private final String appName;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new AppDataLocator with the specified application name and main
|
||||
* application class.
|
||||
*
|
||||
* @param appName The application name.
|
||||
*/
|
||||
public AppDataLocator(String appName) {
|
||||
this.appName = appName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the application data folder path.
|
||||
*
|
||||
* @return the application data folder path.
|
||||
*/
|
||||
public String getAppDataPath() {
|
||||
String userHome = System.getProperty("user.home");
|
||||
String path = "";
|
||||
Path appPath = null;
|
||||
|
||||
if (nonNull(appName)) {
|
||||
path = appName;
|
||||
}
|
||||
|
||||
if (OsInfo.isLinux()) {
|
||||
appPath = Paths.get(userHome, ".config");
|
||||
}
|
||||
else if (OsInfo.isMac()) {
|
||||
appPath = Paths.get(userHome, "Library", "Application Support");
|
||||
}
|
||||
else if (OsInfo.isWindows()) {
|
||||
appPath = Paths.get(userHome, "AppData", "Local");
|
||||
}
|
||||
|
||||
if (nonNull(appPath)) {
|
||||
path = appPath.resolve(path).toString();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the specified sub-path to the complete application data folder
|
||||
* path. Once the complete path has been resolved, the path will end with
|
||||
* the specified sub-path.
|
||||
*
|
||||
* @param subPath The sub-path in the application data folder.
|
||||
*
|
||||
* @return the resolved application data folder path.
|
||||
*/
|
||||
public String toAppDataPath(String subPath) {
|
||||
return getAppDataPath() + File.separator + subPath;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app;
|
||||
|
||||
import moodle.sync.core.ExecutableException;
|
||||
import moodle.sync.core.ExecutableState;
|
||||
|
||||
/**
|
||||
* Sub-classes may implement this interface to provide a consistent mechanism to
|
||||
* start and stop the application.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public interface Application {
|
||||
|
||||
/**
|
||||
* Prepare the application for starting. This method should perform any
|
||||
* initialization required post object creation, optionally using the
|
||||
* arguments provided by the {@code main(String[])} method.
|
||||
*
|
||||
* @param args the main method's arguments.
|
||||
*
|
||||
* @throws ExecutableException If this application detects a fatal error
|
||||
* that prevents this application from being
|
||||
* used.
|
||||
*/
|
||||
void init(final String[] args) throws ExecutableException;
|
||||
|
||||
/**
|
||||
* Responsible for starting the application; e.g. for creating and showing
|
||||
* the initial UI.
|
||||
*
|
||||
* @throws ExecutableException If this application detects a fatal error
|
||||
* that prevents this application from being
|
||||
* used.
|
||||
*/
|
||||
void start() throws ExecutableException;
|
||||
|
||||
/**
|
||||
* Prepare the application to shut down. Subclasses may override this method
|
||||
* to do any cleanup that is necessary before exiting.
|
||||
*
|
||||
* @throws ExecutableException If this application detects a fatal error
|
||||
* that needs to be reported.
|
||||
*/
|
||||
void stop() throws ExecutableException;
|
||||
|
||||
/**
|
||||
* Cleanup resources used by this application.
|
||||
*
|
||||
* @throws ExecutableException If this application detects a fatal error
|
||||
* that prevents this application from being
|
||||
* destroyed.
|
||||
*/
|
||||
void destroy() throws ExecutableException;
|
||||
|
||||
/**
|
||||
* Registers a {@link ApplicationStateListener} on this application.
|
||||
*
|
||||
* @param listener the state listener to be registered.
|
||||
*/
|
||||
void addStateListener(ApplicationStateListener listener);
|
||||
|
||||
/**
|
||||
* Removes a {@link ApplicationStateListener} from this application.
|
||||
*
|
||||
* @param listener the state listener to be removed.
|
||||
*/
|
||||
void removeStateListener(ApplicationStateListener listener);
|
||||
|
||||
/**
|
||||
* Obtain the current state of this application.
|
||||
*
|
||||
* @return The current state of this application.
|
||||
*/
|
||||
ExecutableState getState();
|
||||
|
||||
}
|
|
@ -0,0 +1,493 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app;
|
||||
|
||||
import static java.util.Objects.nonNull;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import moodle.sync.core.ExecutableState;
|
||||
import moodle.sync.core.ExecutableException;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* Base Application implementation that manages the application life cycle
|
||||
* methods. Sub-classes may extend this base class by only implementing the
|
||||
* internal life cycle methods. This base implementation of the {@link
|
||||
* Application} interface handles the proper state transition rules.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public abstract class ApplicationBase implements Application {
|
||||
|
||||
static {
|
||||
try (InputStream stream = ApplicationBase.class.getResourceAsStream("/log.properties")) {
|
||||
if (nonNull(stream)) {
|
||||
java.util.logging.LogManager.getLogManager().readConfiguration(stream);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
/** Logger for {@link ApplicationBase} */
|
||||
private static final Logger LOG = LogManager.getLogger(ApplicationBase.class);
|
||||
|
||||
/** ArrayList containing all open files. */
|
||||
protected static final List<File> OPEN_FILES = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The handler is notified when the application is asked to open a list of
|
||||
* files.
|
||||
*/
|
||||
protected static Consumer<List<File>> openFilesHandler;
|
||||
|
||||
/** The list of all registered state listeners. */
|
||||
private final List<ApplicationStateListener> stateListeners = new ArrayList<>();
|
||||
|
||||
/** The current state of the application. */
|
||||
private ExecutableState state = ExecutableState.Created;
|
||||
|
||||
|
||||
/**
|
||||
* Not to be called directly.
|
||||
* <p>
|
||||
* Subclasses can provide a no-args constructor to initialize the private
|
||||
* final state. Anything else that might refer to public API, should be done
|
||||
* in the {@link #init(String[])} and {@link #start()} method.
|
||||
*/
|
||||
protected ApplicationBase() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public final synchronized void init(final String[] args) throws ExecutableException {
|
||||
setState(ExecutableState.Initializing);
|
||||
|
||||
if (args.length > 0) {
|
||||
// First argument must be the file to open.
|
||||
String fileEncoding = System.getProperty("file.encoding");
|
||||
String utf8Path;
|
||||
|
||||
try {
|
||||
utf8Path = new String(args[0].getBytes(fileEncoding),
|
||||
StandardCharsets.UTF_8);
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
File file = new File(utf8Path);
|
||||
|
||||
if (file.exists()) {
|
||||
OPEN_FILES.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
initInternal(args);
|
||||
}
|
||||
catch (Exception e) {
|
||||
setState(ExecutableState.Error);
|
||||
|
||||
throw new ExecutableException("Failed to initialize Executable component [%s].", e, this);
|
||||
}
|
||||
|
||||
setState(ExecutableState.Initialized);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final synchronized void start() throws ExecutableException {
|
||||
setState(ExecutableState.Starting);
|
||||
|
||||
try {
|
||||
startInternal();
|
||||
}
|
||||
catch (ExecutableException e) {
|
||||
setState(ExecutableState.Error);
|
||||
|
||||
throw new ExecutableException("Failed to start Executable component [%s].", e, this);
|
||||
}
|
||||
|
||||
setState(ExecutableState.Started);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final synchronized void stop() throws ExecutableException {
|
||||
setState(ExecutableState.Stopping);
|
||||
|
||||
try {
|
||||
stopInternal();
|
||||
}
|
||||
catch (ExecutableException e) {
|
||||
setState(ExecutableState.Error);
|
||||
|
||||
throw new ExecutableException("Failed to stop Executable component [%s].", e, this);
|
||||
}
|
||||
|
||||
setState(ExecutableState.Stopped);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final synchronized void destroy() throws ExecutableException {
|
||||
if (state == ExecutableState.Started) {
|
||||
stop();
|
||||
}
|
||||
|
||||
setState(ExecutableState.Destroying);
|
||||
|
||||
try {
|
||||
destroyInternal();
|
||||
}
|
||||
catch (ExecutableException e) {
|
||||
setState(ExecutableState.Error);
|
||||
|
||||
throw new ExecutableException("Failed to destroy Executable component [%s].", e, this);
|
||||
}
|
||||
|
||||
setState(ExecutableState.Destroyed);
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void addStateListener(ApplicationStateListener listener) {
|
||||
requireNonNull(listener, "ApplicationStateListener must not be null.");
|
||||
|
||||
if (!stateListeners.contains(listener)) {
|
||||
stateListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void removeStateListener(ApplicationStateListener listener) {
|
||||
requireNonNull(listener, "ApplicationStateListener must not be null.");
|
||||
|
||||
stateListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final synchronized ExecutableState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify state listeners about the new state.
|
||||
*/
|
||||
protected final void fireStateChanged() {
|
||||
stateListeners.forEach(listener -> listener.applicationState(getState()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ExecutableException If the sub-class fails to initialize this
|
||||
* application.
|
||||
*/
|
||||
protected abstract void initInternal(final String[] args)
|
||||
throws ExecutableException;
|
||||
|
||||
/**
|
||||
* @throws ExecutableException If the sub-class fails to start this
|
||||
* application.
|
||||
*/
|
||||
protected abstract void startInternal() throws ExecutableException;
|
||||
|
||||
/**
|
||||
* @throws ExecutableException If the sub-class fails to stop this
|
||||
* application.
|
||||
*/
|
||||
protected abstract void stopInternal() throws ExecutableException;
|
||||
|
||||
/**
|
||||
* @throws ExecutableException If the sub-class fails to destroy this
|
||||
* application.
|
||||
*/
|
||||
protected abstract void destroyInternal() throws ExecutableException;
|
||||
|
||||
/**
|
||||
* Creates an instance of the concrete {@code Application} subclass, then
|
||||
* calls the sequence of following methods:
|
||||
* <li>{@link #init(String[])}
|
||||
* <li>{@link #start()}
|
||||
* <p>
|
||||
* If a {@link Preloader} class was specified via the system property
|
||||
* "application.preloader", this concrete preloader will be instantiated
|
||||
* prior the application startup routine lasting as long as the application
|
||||
* is initializing. Once the application reaches the starting state, the
|
||||
* preloader will be closed.
|
||||
*
|
||||
* @param args The main method's arguments.
|
||||
*/
|
||||
public static void launch(final String[] args) {
|
||||
try {
|
||||
Class<? extends Preloader> preloaderClass = null;
|
||||
|
||||
String preloaderByProperty = AccessController.doPrivileged((PrivilegedAction<String>) () -> {
|
||||
return System.getProperty("application.preloader");
|
||||
});
|
||||
|
||||
if (nonNull(preloaderByProperty)) {
|
||||
Class<?> pClass = null;
|
||||
|
||||
try {
|
||||
pClass = Class.forName(preloaderByProperty, false, Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.warn("Could not load application preloader class.", e);
|
||||
}
|
||||
|
||||
if (nonNull(pClass)) {
|
||||
if (Preloader.class.isAssignableFrom(pClass)) {
|
||||
preloaderClass = (Class<? extends Preloader>) pClass;
|
||||
}
|
||||
else {
|
||||
LOG.warn("Preloader class is not a subclass of " + Preloader.class.getName() + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
launch(args, preloaderClass);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.fatal("Could not launch application.", e);
|
||||
|
||||
// Hard exit.
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the concrete {@code Application} subclass, then
|
||||
* calls the sequence of following methods:
|
||||
* <li>{@link #init(String[])}
|
||||
* <li>{@link #start()}
|
||||
* <p>
|
||||
* The specified {@link Preloader} class will be instantiated prior the
|
||||
* application startup routine lasting as long as the application is
|
||||
* initializing. Once the application reaches the starting state, the
|
||||
* preloader will be closed.
|
||||
*
|
||||
* @param args The main method's arguments.
|
||||
* @param preloaderClass The class of the preloader to show while the
|
||||
* application is loading.
|
||||
*/
|
||||
public static void launch(final String[] args, Class<? extends Preloader> preloaderClass) {
|
||||
try {
|
||||
StackTraceElement[] cause = Thread.currentThread().getStackTrace();
|
||||
Class<? extends Application> appClass = null;
|
||||
|
||||
for (StackTraceElement se : cause) {
|
||||
String className = se.getClassName();
|
||||
String methodName = se.getMethodName();
|
||||
|
||||
Class<?> callingClass = Class.forName(className, false, Thread.currentThread().getContextClassLoader());
|
||||
|
||||
if ("main".equals(methodName) && Application.class.isAssignableFrom(callingClass)) {
|
||||
appClass = (Class<? extends Application>) callingClass;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
requireNonNull(appClass, "No application class found.");
|
||||
|
||||
launch(args, appClass, preloaderClass);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.fatal("Could not launch application.", e);
|
||||
|
||||
// Hard exit.
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void launch(final String[] args,
|
||||
Class<? extends Application> appClass,
|
||||
Class<? extends Preloader> preloaderClass) throws Exception {
|
||||
requireNonNull(appClass, "Application class must not be null.");
|
||||
|
||||
Preloader preloader = null;
|
||||
|
||||
if (nonNull(preloaderClass)) {
|
||||
try {
|
||||
preloader = preloaderClass.getConstructor().newInstance();
|
||||
preloader.init(args);
|
||||
preloader.start();
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.warn("Start preloader failed.", e);
|
||||
}
|
||||
}
|
||||
|
||||
StateListener stateListener = new StateListener(preloader);
|
||||
|
||||
Application application = appClass.getConstructor().newInstance();
|
||||
application.addStateListener(stateListener);
|
||||
application.init(args);
|
||||
application.start();
|
||||
|
||||
if (nonNull(stateListener.getException())) {
|
||||
throw stateListener.getException();
|
||||
}
|
||||
|
||||
application.removeStateListener(stateListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the component state if, and only if, the attempted state
|
||||
* transition is valid.
|
||||
*
|
||||
* @param state The new state for this component.
|
||||
*
|
||||
* @throws ExecutableException If the state transition fails.
|
||||
*/
|
||||
private synchronized void setState(ExecutableState state) throws ExecutableException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Setting state for [{}] to [{}]", this, state);
|
||||
}
|
||||
|
||||
if (!validateNextState(state)) {
|
||||
throw new ExecutableException(
|
||||
"Invalid state transition for Executable component [%s] in state [%s] to [%s].",
|
||||
this, getState(), state);
|
||||
}
|
||||
|
||||
this.state = state;
|
||||
|
||||
fireStateChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@code nextState} is allowed the be the next state after the current {@code state} of the application.
|
||||
*
|
||||
* @param nextState The state that is desired to be the next state.
|
||||
* @return true if {@code nextState} is allowed the be the next state after the current {@code state} of
|
||||
* the application and false otherwise.
|
||||
*/
|
||||
private boolean validateNextState(ExecutableState nextState) {
|
||||
switch (this.state) {
|
||||
case Created:
|
||||
return isAllowed(nextState, ExecutableState.Initializing, ExecutableState.Destroying);
|
||||
|
||||
case Initializing:
|
||||
return isAllowed(nextState, ExecutableState.Initialized, ExecutableState.Error);
|
||||
|
||||
case Initialized:
|
||||
return isAllowed(nextState, ExecutableState.Starting, ExecutableState.Destroying);
|
||||
|
||||
case Starting:
|
||||
return isAllowed(nextState, ExecutableState.Started, ExecutableState.Error);
|
||||
|
||||
case Started:
|
||||
return isAllowed(nextState, ExecutableState.Stopping, ExecutableState.Destroying);
|
||||
|
||||
case Stopping:
|
||||
return isAllowed(nextState, ExecutableState.Stopped, ExecutableState.Error);
|
||||
|
||||
case Stopped:
|
||||
return isAllowed(nextState, ExecutableState.Starting, ExecutableState.Destroying);
|
||||
|
||||
case Destroying:
|
||||
return isAllowed(nextState, ExecutableState.Destroyed, ExecutableState.Error);
|
||||
|
||||
case Destroyed:
|
||||
return isAllowed(nextState, ExecutableState.Initializing);
|
||||
|
||||
case Error:
|
||||
// Allow to recover from previous operation failure.
|
||||
return isAllowed(nextState, ExecutableState.Starting, ExecutableState.Stopping, ExecutableState.Destroying);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@code nextState} is a allowed to be the next state.
|
||||
*
|
||||
* @param nextState The state that is desired to be the next state.
|
||||
* @param allowedStates All the states that are possible to be the next state.
|
||||
* @return {@code true} if {@code allowedStates} contains {@code nextState}, otherwise {@code false}.
|
||||
*/
|
||||
private boolean isAllowed(ExecutableState nextState, ExecutableState... allowedStates) {
|
||||
requireNonNull(allowedStates, "No allowed states provided.");
|
||||
|
||||
for (ExecutableState allowedState : allowedStates) {
|
||||
if (nextState == allowedState) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class StateListener implements ApplicationStateListener {
|
||||
|
||||
/** The stateListeners preloader */
|
||||
private final Preloader preloader;
|
||||
|
||||
/** Possible exception caught in {@link #applicationState(ExecutableState)} */
|
||||
private Exception exception;
|
||||
|
||||
/**
|
||||
* Create a new {@link StateListener} with the specified preloader.
|
||||
*
|
||||
* @param preloader the currently active preloader
|
||||
*/
|
||||
StateListener(Preloader preloader) {
|
||||
this.preloader = preloader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applicationState(ExecutableState state) {
|
||||
if (state == ExecutableState.Starting) {
|
||||
if (nonNull(preloader)) {
|
||||
try {
|
||||
preloader.close();
|
||||
preloader.destroy();
|
||||
}
|
||||
catch (Exception e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the exception of this {@link StateListener}.
|
||||
*
|
||||
* @return The exception of this {@link StateListener}.
|
||||
*/
|
||||
public Exception getException() {
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app;
|
||||
|
||||
|
||||
import moodle.sync.core.app.configuration.Configuration;
|
||||
import moodle.sync.core.app.dictionary.Dictionary;
|
||||
import moodle.sync.core.beans.BooleanProperty;
|
||||
import moodle.sync.core.bus.EventBus;
|
||||
|
||||
import moodle.sync.core.geometry.Position;
|
||||
import moodle.sync.core.presenter.command.ConfirmationNotificationCommand;
|
||||
import moodle.sync.core.presenter.command.NotificationCommand;
|
||||
import moodle.sync.core.presenter.command.NotificationPopupCommand;
|
||||
import moodle.sync.core.view.Action;
|
||||
import moodle.sync.core.view.NotificationType;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Base application context implementation that holds data object required by
|
||||
* the application. Such objects are, for instance, the {@link Configuration},
|
||||
* the {@link Dictionary}
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public abstract class ApplicationContext {
|
||||
|
||||
/** Indicates whether the application is in fullscreen mode. */
|
||||
private final BooleanProperty fullscreen = new BooleanProperty();
|
||||
|
||||
/** The application resource data locator. */
|
||||
private final AppDataLocator dataLocator;
|
||||
|
||||
/** The application configuration. */
|
||||
private final Configuration configuration;
|
||||
|
||||
/** The application dictionary. */
|
||||
private final Dictionary dictionary;
|
||||
|
||||
/** The application event data bus. */
|
||||
private final EventBus eventBus;
|
||||
|
||||
/** The audio event bus. */
|
||||
private final EventBus audioBus;
|
||||
|
||||
|
||||
/**
|
||||
* This method is meant to be implemented by concrete application context
|
||||
* class that implement their own configuration handling, like specific
|
||||
* configuration paths and names.
|
||||
*
|
||||
* @throws Exception If a fatal error occurs while saving the configuration.
|
||||
*/
|
||||
public abstract void saveConfiguration() throws Exception;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationContext} instance with the given parameters.
|
||||
*
|
||||
* @param dataLocator The application resource data locator.
|
||||
* @param config The application configuration.
|
||||
* @param dict The application dictionary.
|
||||
* @param eventBus The application event data bus.
|
||||
* @param audioBus The audio event bus.
|
||||
*/
|
||||
protected ApplicationContext(AppDataLocator dataLocator, Configuration config,
|
||||
Dictionary dict, EventBus eventBus, EventBus audioBus) {
|
||||
this.dataLocator = dataLocator;
|
||||
this.configuration = config;
|
||||
this.dictionary = dict;
|
||||
this.eventBus = eventBus;
|
||||
this.audioBus = audioBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the application configuration.
|
||||
*
|
||||
* @return the application configuration.
|
||||
*/
|
||||
public Configuration getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the application dictionary.
|
||||
*
|
||||
* @return the application dictionary.
|
||||
*/
|
||||
public Dictionary getDictionary() {
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the application event data bus.
|
||||
*
|
||||
* @return the application event data bus.
|
||||
*/
|
||||
public EventBus getEventBus() {
|
||||
return eventBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the audio event bus.
|
||||
*
|
||||
* @return the audio event bus.
|
||||
*/
|
||||
public EventBus getAudioBus() {
|
||||
return audioBus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the AppDataLocator to access application specific data.
|
||||
*
|
||||
* @return the AppDataLocator.
|
||||
*/
|
||||
public AppDataLocator getDataLocator() {
|
||||
return dataLocator;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Puts the application in full screen mode.
|
||||
*
|
||||
* @param active True to set full screen mode.
|
||||
*/
|
||||
public void setFullscreen(boolean active) {
|
||||
fullscreen.set(active);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the observable fullscreen property.
|
||||
*
|
||||
* @return The fullscreen property.
|
||||
*/
|
||||
public BooleanProperty fullscreenProperty() {
|
||||
return fullscreen;
|
||||
}
|
||||
|
||||
|
||||
public final void showError(String title, String message) {
|
||||
requireNonNull(title);
|
||||
|
||||
showNotification(NotificationType.ERROR, title, message);
|
||||
}
|
||||
|
||||
public final void showError(String title, String message, Object... messageParams) {
|
||||
showNotification(NotificationType.ERROR, title, message, messageParams);
|
||||
}
|
||||
|
||||
public final void showNotification(NotificationType type, String title, String message) {
|
||||
if (getDictionary().contains(title)) {
|
||||
title = getDictionary().get(title);
|
||||
}
|
||||
if (getDictionary().contains(message)) {
|
||||
message = getDictionary().get(message);
|
||||
}
|
||||
|
||||
getEventBus().post(new NotificationCommand(type, title, message));
|
||||
}
|
||||
|
||||
public final void showNotification(NotificationType type, String title, String message, Object... messageParams) {
|
||||
if (getDictionary().contains(message)) {
|
||||
message = getDictionary().get(message);
|
||||
}
|
||||
|
||||
message = MessageFormat.format(message, messageParams);
|
||||
|
||||
showNotification(type, title, message);
|
||||
}
|
||||
|
||||
public final void showNotificationPopup(String title) {
|
||||
showNotificationPopup(title, null);
|
||||
}
|
||||
|
||||
public final void showNotificationPopup(String title, String message) {
|
||||
if (getDictionary().contains(title)) {
|
||||
title = getDictionary().get(title);
|
||||
}
|
||||
if (getDictionary().contains(message)) {
|
||||
message = getDictionary().get(message);
|
||||
}
|
||||
|
||||
getEventBus().post(new NotificationPopupCommand(Position.TOP_RIGHT, title, message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a notification pop with an accept and decline option.
|
||||
*
|
||||
* @param type The Notification Type
|
||||
* @param title The title of the notification
|
||||
* @param message The message of the notification
|
||||
* @param confirmAction The action when the user clicks the confirm button
|
||||
* @param discardAction The action when the user clicks the close button
|
||||
*/
|
||||
public final void showConfirmationNotification(NotificationType type, String title, String message,
|
||||
Action confirmAction, Action discardAction) {
|
||||
showConfirmationNotification(type, title, message, confirmAction, discardAction, "button.confirm", "button.close");
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a notification pop with an accept and decline option.
|
||||
*
|
||||
* @param type The Notification Type
|
||||
* @param title The title of the notification
|
||||
* @param message The message of the notification
|
||||
* @param confirmAction The action when the user clicks the confirm button
|
||||
* @param discardAction The action when the user clicks the close button
|
||||
*/
|
||||
public final void showConfirmationNotification(NotificationType type, String title, String message,
|
||||
Action confirmAction, Action discardAction,
|
||||
String confirmButtonText, String discardButtonText) {
|
||||
if (getDictionary().contains(title)) {
|
||||
title = getDictionary().get(title);
|
||||
}
|
||||
if (getDictionary().contains(message)) {
|
||||
message = getDictionary().get(message);
|
||||
}
|
||||
if (getDictionary().contains(confirmButtonText)) {
|
||||
confirmButtonText = getDictionary().get(confirmButtonText);
|
||||
}
|
||||
if (getDictionary().contains(discardButtonText)) {
|
||||
discardButtonText = getDictionary().get(discardButtonText);
|
||||
}
|
||||
|
||||
|
||||
getEventBus().post(new ConfirmationNotificationCommand(type, title, message, confirmAction,
|
||||
discardAction, confirmButtonText, discardButtonText));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app;
|
||||
|
||||
import moodle.sync.core.presenter.MainPresenter;
|
||||
|
||||
/**
|
||||
* Common interface to provide a consistent mechanism for creating an
|
||||
* application.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public interface ApplicationFactory {
|
||||
|
||||
/**
|
||||
* Create the application specific ApplicationContext.
|
||||
*
|
||||
* @return the application specific ApplicationContext.
|
||||
*/
|
||||
ApplicationContext getApplicationContext();
|
||||
|
||||
/**
|
||||
* Create the start presenter that will initialize the initial (start) view
|
||||
* of the application.
|
||||
*
|
||||
* @return the start context.
|
||||
*/
|
||||
MainPresenter<?> getStartPresenter();
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app;
|
||||
|
||||
import moodle.sync.core.ExecutableState;
|
||||
|
||||
/**
|
||||
* A listener for application state transition events. The listener will be
|
||||
* notified after a state transition has taken place.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public interface ApplicationStateListener {
|
||||
|
||||
/**
|
||||
* Receive the state transition event.
|
||||
*
|
||||
* @param state The new state of the application.
|
||||
*/
|
||||
void applicationState(ExecutableState state);
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app;
|
||||
|
||||
/**
|
||||
* Common interface to provide a consistent mechanism for creating a graphical
|
||||
* application.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public interface GraphicalApplication {
|
||||
|
||||
/**
|
||||
* Create the application specific {@link ApplicationFactory} to bootstrap
|
||||
* the application.
|
||||
*
|
||||
* @return the {@link ApplicationFactory}.
|
||||
*/
|
||||
ApplicationFactory createApplicationFactory();
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import moodle.sync.core.util.FileUtils;
|
||||
|
||||
/**
|
||||
* Locale helper class to conveniently handle application locales. Application
|
||||
* internationalisation dictionaries are located in "i18n" resource folders.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class LocaleProvider {
|
||||
|
||||
/**
|
||||
* Get a list of supported locales which are based on the files located
|
||||
* in the "i18n" folder.
|
||||
*
|
||||
* @return the available localizations.
|
||||
*/
|
||||
public List<Locale> getLocales() throws Exception {
|
||||
List<Locale> locales = new ArrayList<>();
|
||||
|
||||
// Load only files which have '.properties' as extension.
|
||||
String[] listing = FileUtils.getResourceListing("/resources/i18n",
|
||||
(name) -> name.endsWith(".properties"));
|
||||
|
||||
for (String fileName : listing) {
|
||||
String tag = fileName.substring(0, fileName.lastIndexOf("."));
|
||||
tag = tag.substring(tag.indexOf("_") + 1);
|
||||
tag = tag.replace("_", "-");
|
||||
|
||||
locales.add(Locale.forLanguageTag(tag));
|
||||
}
|
||||
|
||||
return locales;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the locale that best matches the specified locale. If no match can be
|
||||
* found, the first available locale is returned.
|
||||
*
|
||||
* @param locale The locale for which to find the best match.
|
||||
*
|
||||
* @return The locale that best matches the provided locale.
|
||||
*
|
||||
* @throws Exception - if the application locales could not be loaded.
|
||||
*/
|
||||
public Locale getBestSupported(Locale locale) throws Exception {
|
||||
List<Locale> locales = getLocales();
|
||||
|
||||
// Try to find an exact match.
|
||||
var result = locales.stream().filter(l -> l.equals(locale)).findFirst();
|
||||
if (result.isPresent()) {
|
||||
return result.get();
|
||||
}
|
||||
|
||||
// Compare by language.
|
||||
result = locales.stream()
|
||||
.filter(l -> l.getLanguage().equals(locale.getLanguage()))
|
||||
.findFirst();
|
||||
|
||||
return result.orElseGet(() -> locales.get(0));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app;
|
||||
|
||||
import moodle.sync.core.ExecutableException;
|
||||
|
||||
/**
|
||||
* Common interface to provide a consistent mechanism for creating a graphical
|
||||
* preloader for an application.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public interface Preloader {
|
||||
|
||||
/**
|
||||
* Prepare the preloader for starting. This method should perform any
|
||||
* initialization required post object creation, optionally using the
|
||||
* arguments provided by the {@code main(String[])} method.
|
||||
*
|
||||
* @param args The main method's arguments.
|
||||
*
|
||||
* @throws ExecutableException If a fatal error occurred that prevents this
|
||||
* preloader from being used.
|
||||
*/
|
||||
void init(final String[] args) throws ExecutableException;
|
||||
|
||||
/**
|
||||
* Start the preloader and show the UI.
|
||||
*
|
||||
* @throws ExecutableException If a fatal error occurred that prevents this
|
||||
* preloader from being used.
|
||||
*/
|
||||
void start() throws ExecutableException;
|
||||
|
||||
/**
|
||||
* Close the preloader and hide the UI.
|
||||
*
|
||||
* @throws ExecutableException If a fatal error occurred that prevents this
|
||||
* preloader from being closed.
|
||||
*/
|
||||
void close() throws ExecutableException;
|
||||
|
||||
/**
|
||||
* Cleanup resources used by this preloader.
|
||||
*
|
||||
* @throws ExecutableException If a fatal error occurred that prevents this
|
||||
* preloader from being destroyed.
|
||||
*/
|
||||
void destroy() throws ExecutableException;
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A Theme defines the graphical appearance of the user interface. Themes can be
|
||||
* used to customize the the look and feel of the window and its graphical
|
||||
* control elements. Themes are loaded from theme files.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class Theme {
|
||||
|
||||
/** The theme name. */
|
||||
private String name;
|
||||
|
||||
/** The theme file. */
|
||||
private String file;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link Theme} with empty name.
|
||||
*/
|
||||
public Theme() {
|
||||
this("", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Theme} with the specified name and source file.
|
||||
*
|
||||
* @param name The name of the theme.
|
||||
* @param file The file containing theme definitions.
|
||||
*/
|
||||
public Theme(String name, String file) {
|
||||
this.name = name;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the theme name.
|
||||
*
|
||||
* @return the theme name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the theme file.
|
||||
*
|
||||
* @return the theme file.
|
||||
*/
|
||||
public String getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (other == null || getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Theme theme = (Theme) other;
|
||||
|
||||
return name.equals(theme.name) && Objects.equals(file, theme.file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, file);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app.configuration;
|
||||
|
||||
import moodle.sync.core.app.Theme;
|
||||
import moodle.sync.core.beans.BooleanProperty;
|
||||
import moodle.sync.core.beans.DoubleProperty;
|
||||
import moodle.sync.core.beans.ObjectProperty;
|
||||
import moodle.sync.core.beans.StringProperty;
|
||||
import moodle.sync.core.geometry.Dimension2D;
|
||||
import moodle.sync.core.util.ObservableHashMap;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* The Configuration specifies application wide properties. Context specific
|
||||
* properties are encapsulated in the respective separate configuration classes.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class Configuration {
|
||||
|
||||
/** The name of the application. */
|
||||
private final StringProperty applicationName = new StringProperty();
|
||||
|
||||
/** The theme of the UI of the application. */
|
||||
private final ObjectProperty<Theme> theme = new ObjectProperty<>();
|
||||
|
||||
/** The locale of the application. */
|
||||
private final ObjectProperty<Locale> locale = new ObjectProperty<>();
|
||||
|
||||
/** Indicates whether to check for a new version of the application. */
|
||||
private final BooleanProperty checkNewVersion = new BooleanProperty();
|
||||
|
||||
/** The UI control size of the application. */
|
||||
private final DoubleProperty uiControlSize = new DoubleProperty();
|
||||
|
||||
/** Indicates whether to open the application window maximized. */
|
||||
private final BooleanProperty startMaximized = new BooleanProperty();
|
||||
|
||||
/** Indicates whether to open the application in fullscreen mode. */
|
||||
private final BooleanProperty startFullscreen = new BooleanProperty();
|
||||
|
||||
/** Indicates whether to use native mouse input instead of pen/stylus input. */
|
||||
private final BooleanProperty useMouseInput = new BooleanProperty();
|
||||
|
||||
/** Indicates whether to enable a virtual keyboard on tablet devices. */
|
||||
private final BooleanProperty tabletMode = new BooleanProperty();
|
||||
|
||||
/** Enables/disables advanced settings visible in the settings UI view. */
|
||||
private final BooleanProperty advancedUIMode = new BooleanProperty();
|
||||
|
||||
/** Hides/shows UI elements, like the menu, in fullscreen mode. */
|
||||
private final BooleanProperty extendedFullscreen = new BooleanProperty();
|
||||
|
||||
/** Defines the extended drawing area of a page. */
|
||||
private final ObjectProperty<Dimension2D> extendPageDimension = new ObjectProperty<>();
|
||||
|
||||
/** The mapping of a filesystem path to a related context. */
|
||||
private final ObservableHashMap<String, String> contextPaths = new ObservableHashMap<>();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the name of the application.
|
||||
*
|
||||
* @return the application name.
|
||||
*/
|
||||
public String getApplicationName() {
|
||||
return applicationName.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the application.
|
||||
*
|
||||
* @param name The application name.
|
||||
*/
|
||||
public void setApplicationName(String name) {
|
||||
this.applicationName.set(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the application name property.
|
||||
*
|
||||
* @return the application name property.
|
||||
*/
|
||||
public ObjectProperty<String> applicationNameProperty() {
|
||||
return applicationName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the current theme of the UI of the application.
|
||||
*
|
||||
* @return the UI theme.
|
||||
*/
|
||||
public Theme getTheme() {
|
||||
return theme.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new UI theme.
|
||||
*
|
||||
* @param theme The UI theme to set.
|
||||
*/
|
||||
public void setTheme(Theme theme) {
|
||||
this.theme.set(theme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the theme property.
|
||||
*
|
||||
* @return the theme property.
|
||||
*/
|
||||
public ObjectProperty<Theme> themeProperty() {
|
||||
return theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the current locale of the application.
|
||||
*
|
||||
* @return the current locale.
|
||||
*/
|
||||
public Locale getLocale() {
|
||||
return locale.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new locale of the application.
|
||||
*
|
||||
* @param locale The new locale to set.
|
||||
*/
|
||||
public void setLocale(Locale locale) {
|
||||
this.locale.set(locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the locale property.
|
||||
*
|
||||
* @return the locale property.
|
||||
*/
|
||||
public ObjectProperty<Locale> localeProperty() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain whether new version checking is enabled.
|
||||
*
|
||||
* @return {@code true} if version checking is enabled, otherwise {@code false}.
|
||||
*/
|
||||
public boolean getCheckNewVersion() {
|
||||
return checkNewVersion.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to check for new versions of the application.
|
||||
*
|
||||
* @param check True to check for new versions.
|
||||
*/
|
||||
public void setCheckNewVersion(boolean check) {
|
||||
this.checkNewVersion.set(check);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the property for new version checking.
|
||||
*
|
||||
* @return the new version checking property.
|
||||
*/
|
||||
public BooleanProperty checkNewVersionProperty() {
|
||||
return checkNewVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the UI control size of the application.
|
||||
*
|
||||
* @return the UI control size.
|
||||
*/
|
||||
public double getUIControlSize() {
|
||||
return uiControlSize.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new UI control size of the application.
|
||||
*
|
||||
* @param size The new UI control size.
|
||||
*/
|
||||
public void setUIControlSize(double size) {
|
||||
this.uiControlSize.set(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the UI control size property.
|
||||
*
|
||||
* @return the UI control size property.
|
||||
*/
|
||||
public DoubleProperty uiControlSizeProperty() {
|
||||
return uiControlSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether to open the application window maximized.
|
||||
*
|
||||
* @return {@code true} if the application window should be opened maximized, otherwise {@code false}.
|
||||
*/
|
||||
public Boolean getStartMaximized() {
|
||||
return startMaximized.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to open the application window maximized.
|
||||
*
|
||||
* @param maximized True to open the application window maximized, false
|
||||
* otherwise.
|
||||
*/
|
||||
public void setStartMaximized(boolean maximized) {
|
||||
this.startMaximized.set(maximized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the start maximized property.
|
||||
*
|
||||
* @return the start maximized property.
|
||||
*/
|
||||
public BooleanProperty startMaximizedProperty() {
|
||||
return startMaximized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether to open the application window in fullscreen mode.
|
||||
*
|
||||
* @return {@code true} if the application window should be opened fullscreen, otherwise {@code false}.
|
||||
*/
|
||||
public Boolean getStartFullscreen() {
|
||||
return startFullscreen.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to open the application window in fullscreen mode.
|
||||
*
|
||||
* @param fullscreen True to open the application window fullscreen, false
|
||||
* otherwise.
|
||||
*/
|
||||
public void setStartFullscreen(boolean fullscreen) {
|
||||
this.startFullscreen.set(fullscreen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the start fullscreen property.
|
||||
*
|
||||
* @return the start fullscreen property.
|
||||
*/
|
||||
public BooleanProperty startFullscreenProperty() {
|
||||
return startFullscreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether to use app native mouse input instead of the pen/stylus
|
||||
* input.
|
||||
*
|
||||
* @return {@code true} if the application should use mouse input.
|
||||
*/
|
||||
public Boolean getUseMouseInput() {
|
||||
return useMouseInput.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use app native mouse input instead of the pen/stylus
|
||||
* input.
|
||||
*
|
||||
* @param useMouse True to use mouse input.
|
||||
*/
|
||||
public void setUseMouseInput(boolean useMouse) {
|
||||
this.useMouseInput.set(useMouse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the mouse input property.
|
||||
*
|
||||
* @return the start mouse input property.
|
||||
*/
|
||||
public BooleanProperty useMouseInputProperty() {
|
||||
return useMouseInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether to enable a virtual keyboard on tablet devices.
|
||||
*
|
||||
* @return {@code true} to enable a virtual keyboard, false otherwise.
|
||||
*/
|
||||
public Boolean getTabletMode() {
|
||||
return tabletMode.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to enable a virtual keyboard on tablet devices.
|
||||
*
|
||||
* @param enable True to enable a virtual keyboard, false otherwise.
|
||||
*/
|
||||
public void setTabletMode(boolean enable) {
|
||||
this.tabletMode.set(enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the tablet mode property.
|
||||
*
|
||||
* @return the tablet mode property.
|
||||
*/
|
||||
public BooleanProperty tabletModeProperty() {
|
||||
return tabletMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether to hide/show UI elements, like the menu, in fullscreen
|
||||
* mode.
|
||||
*
|
||||
* @return {@code true} if the extended fullscreen mode is enabled, otherwise {@code false}.
|
||||
*
|
||||
* @see #setExtendedFullscreen(boolean)
|
||||
*/
|
||||
public Boolean getExtendedFullscreen() {
|
||||
return extendedFullscreen.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to hide/show UI elements, like the menu, in fullscreen mode.
|
||||
* <p>
|
||||
* If the value is set to {@code true}, then the related UI elements must be
|
||||
* hidden when fullscreen is activated, and shown again when leaving the
|
||||
* fullscreen mode.
|
||||
*
|
||||
* @param enabled True to enable the extended fullscreen mode, false
|
||||
* otherwise.
|
||||
*/
|
||||
public void setExtendedFullscreen(boolean enabled) {
|
||||
this.extendedFullscreen.set(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the extended fullscreen mode property.
|
||||
*
|
||||
* @return the extended fullscreen mode property.
|
||||
*/
|
||||
public BooleanProperty extendedFullscreenProperty() {
|
||||
return extendedFullscreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether to enable/disable advanced settings visible in the settings
|
||||
* UI view.
|
||||
*
|
||||
* @return {@code true} if the advanced settings mode is enabled, otherwise {@code false}.
|
||||
*
|
||||
* @see #setAdvancedUIMode(Boolean)
|
||||
*/
|
||||
public Boolean getAdvancedUIMode() {
|
||||
return advancedUIMode.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to enable/disable advanced settings visible in the settings
|
||||
* UI view.
|
||||
* <p>
|
||||
* If the value is set to {@code true}, then the advanced settings UI
|
||||
* elements must be visible in the settings UI view, and hidden again when
|
||||
* the advanced settings mode is disabled.
|
||||
*
|
||||
* @param enabled True to enable the advanced settings mode, false
|
||||
* otherwise.
|
||||
*/
|
||||
public void setAdvancedUIMode(Boolean enabled) {
|
||||
this.advancedUIMode.set(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the advanced settings mode property.
|
||||
*
|
||||
* @return the advanced settings mode property.
|
||||
*/
|
||||
public BooleanProperty advancedUIModeProperty() {
|
||||
return advancedUIMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the extended drawing area of a page.
|
||||
*
|
||||
* @return the new extended drawing area.
|
||||
*
|
||||
* @see #setExtendPageDimension(Dimension2D)
|
||||
*/
|
||||
public Dimension2D getExtendPageDimension() {
|
||||
return extendPageDimension.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new extended drawing area of a page. The specified dimension
|
||||
* defines how the page is scaled down in order to provide additional blank
|
||||
* drawing area. The width and height of the specified dimension must be in
|
||||
* the range of [0,1].
|
||||
*
|
||||
* @param dimension The new extended page dimension to set.
|
||||
*/
|
||||
public void setExtendPageDimension(Dimension2D dimension) {
|
||||
this.extendPageDimension.set(dimension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the extended page dimension property.
|
||||
*
|
||||
* @return the extended page dimension property.
|
||||
*/
|
||||
public ObjectProperty<Dimension2D> extendPageDimensionProperty() {
|
||||
return extendPageDimension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mapping of a filesystem path to a related context.
|
||||
*
|
||||
* @return The context to path mapping.
|
||||
*/
|
||||
public ObservableHashMap<String, String> getContextPaths() {
|
||||
return contextPaths;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app.configuration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Common interface to provide a consistent mechanism for loading and saving
|
||||
* application configurations that are stored in the operating systems file
|
||||
* system.
|
||||
*
|
||||
* @param <T> The type of the configuration.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public interface ConfigurationService<T> {
|
||||
|
||||
/**
|
||||
* Loads a configuration of the specified class type from the specified
|
||||
* file.
|
||||
*
|
||||
* @param file The file containing the configuration values.
|
||||
* @param cls The class of the configuration to create.
|
||||
*
|
||||
* @return an instance of the specified configuration type.
|
||||
*
|
||||
* @throws IOException If an fatal error occurred while loading the
|
||||
* configuration file.
|
||||
*/
|
||||
T load(File file, Class<T> cls) throws IOException;
|
||||
|
||||
/**
|
||||
* Save a configuration object of the specified type to the specified file.
|
||||
*
|
||||
* @param file The destination file of the configuration.
|
||||
* @param config The configuration object.
|
||||
*
|
||||
* @throws IOException If an fatal error occurred while saving the
|
||||
* configuration file.
|
||||
*/
|
||||
void save(File file, T config) throws IOException;
|
||||
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app.configuration;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Calendar;
|
||||
|
||||
import moodle.sync.core.app.configuration.bind.CalendarDeserializer;
|
||||
import moodle.sync.core.app.configuration.bind.ColorDeserializer;
|
||||
import moodle.sync.core.app.configuration.bind.ColorSerializer;
|
||||
import moodle.sync.core.app.configuration.bind.Rectangle2DMixin;
|
||||
import moodle.sync.core.geometry.Rectangle2D;
|
||||
import moodle.sync.core.graphics.Color;
|
||||
import moodle.sync.core.util.ObservableArrayList;
|
||||
import moodle.sync.core.util.ObservableHashSet;
|
||||
import moodle.sync.core.util.ObservableList;
|
||||
import moodle.sync.core.util.ObservableSet;
|
||||
|
||||
import static java.util.Objects.nonNull;
|
||||
|
||||
/**
|
||||
* ConfigurationService implementation for loading and saving configuration
|
||||
* files in the JSON format.
|
||||
*
|
||||
* @param <T> The type of the configuration.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class JsonConfigurationService<T> implements ConfigurationService<T> {
|
||||
|
||||
/**
|
||||
* The object mapper that configures the conversion to and from the JSON
|
||||
* format.
|
||||
*/
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new JsonConfigurationService instance.
|
||||
*/
|
||||
public JsonConfigurationService() {
|
||||
SimpleModule module = new SimpleModule();
|
||||
module.addAbstractTypeMapping(ObservableList.class, ObservableArrayList.class);
|
||||
module.addAbstractTypeMapping(ObservableSet.class, ObservableHashSet.class);
|
||||
module.addSerializer(Color.class, new ColorSerializer());
|
||||
module.addDeserializer(Color.class, new ColorDeserializer());
|
||||
module.addDeserializer(Calendar.class, new CalendarDeserializer());
|
||||
module.setMixInAnnotation(Rectangle2D.class, Rectangle2DMixin.class);
|
||||
|
||||
mapper = new ObjectMapper();
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
mapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
mapper.registerModules(new Jdk8Module(), new JavaTimeModule());
|
||||
mapper.registerModule(module);
|
||||
|
||||
initModules(mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T load(File file, Class<T> cls) throws IOException {
|
||||
T config;
|
||||
InputStream input = null;
|
||||
|
||||
try {
|
||||
if (file.exists()) {
|
||||
input = new FileInputStream(file);
|
||||
}
|
||||
else {
|
||||
input = getClass().getResourceAsStream(file.getPath().replace("\\", "/"));
|
||||
}
|
||||
|
||||
if (input == null) {
|
||||
throw new IOException("Unable to load configuration file. File does not exist.");
|
||||
}
|
||||
|
||||
config = mapper.readValue(input, cls);
|
||||
}
|
||||
finally {
|
||||
if (input != null) {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(File file, T config) throws IOException {
|
||||
File parent = file.getParentFile();
|
||||
|
||||
if (nonNull(parent) && !parent.exists()) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
|
||||
mapper.writeValue(file, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the provided configuration. Can be used in order to check for
|
||||
* mandatory properties and set them accordingly.
|
||||
*
|
||||
* @param config The config to validate.
|
||||
*/
|
||||
public void validate(T config) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Meant to be overridden by sub-classes to add custom modules to the object
|
||||
* mapper.
|
||||
*
|
||||
* @param mapper The JSON object mapper.
|
||||
*/
|
||||
protected void initModules(ObjectMapper mapper) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app.configuration.bind;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* Implementation of a {@link Calendar} JSON deserializer.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class CalendarDeserializer extends JsonDeserializer<Calendar> {
|
||||
|
||||
@Override
|
||||
public Calendar deserialize(JsonParser parser, DeserializationContext context) throws IOException {
|
||||
JsonNode node = parser.getCodec().readTree(parser);
|
||||
|
||||
if (!node.canConvertToLong()) {
|
||||
throw new IOException("Deserialize calendar failed.");
|
||||
}
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTimeInMillis(node.longValue());
|
||||
|
||||
return calendar;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app.configuration.bind;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import moodle.sync.core.graphics.Color;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Implementation of a {@link Color} JSON deserializer.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class ColorDeserializer extends JsonDeserializer<Color> {
|
||||
|
||||
@Override
|
||||
public Color deserialize(JsonParser parser, DeserializationContext context) throws IOException {
|
||||
JsonNode node = parser.getCodec().readTree(parser);
|
||||
|
||||
if (!node.canConvertToInt()) {
|
||||
throw new IOException("Deserialize color failed.");
|
||||
}
|
||||
|
||||
return new Color(node.asInt());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app.configuration.bind;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import moodle.sync.core.graphics.Color;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Implementation of a {@link Color} JSON serializer.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class ColorSerializer extends JsonSerializer<Color> {
|
||||
|
||||
@Override
|
||||
public void serialize(Color color, JsonGenerator generator, SerializerProvider serializers) throws IOException {
|
||||
generator.writeNumber(color.getRGBA());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app.configuration.bind;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import moodle.sync.core.geometry.Point2D;
|
||||
import moodle.sync.core.geometry.Rectangle2D;
|
||||
|
||||
/**
|
||||
* A {@link Rectangle2D} mixin-class to be registered with the FasterXML/jackson
|
||||
* ObjectMapper. This annotation class defines methods to be ignored while
|
||||
* serializing and deserializing Rectangle2D objects.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public abstract class Rectangle2DMixin {
|
||||
|
||||
@JsonIgnore
|
||||
abstract Point2D getLocation();
|
||||
|
||||
@JsonIgnore
|
||||
abstract boolean isEmpty();
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app.dictionary;
|
||||
|
||||
/**
|
||||
* The Dictionary provides an interface to a string translation dictionary. The
|
||||
* dictionary format and storage method depends on the implementation.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public interface Dictionary {
|
||||
|
||||
/**
|
||||
* Returns the value to which the key is mapped in this dictionary. If the
|
||||
* dictionary contains an entry for the specified key, the associated value
|
||||
* is returned, otherwise {@code null} is returned.
|
||||
*
|
||||
* @param key A key in the dictionary.
|
||||
*
|
||||
* @return the value associated with the specified key.
|
||||
*
|
||||
* @throws NullPointerException If the key is {@code null}.
|
||||
*/
|
||||
String get(String key) throws NullPointerException;
|
||||
|
||||
/**
|
||||
* Checks the dictionary for an existing key.
|
||||
*
|
||||
* @param key A key in the dictionary.
|
||||
*
|
||||
* @return {@code true} if the dictionary contains an entry for the specified key, otherwise {@code false}.
|
||||
*/
|
||||
boolean contains(String key);
|
||||
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.app.dictionary;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Dictionary implementation for loading dictionary translation files in the
|
||||
* properties format.
|
||||
*
|
||||
* @see ResourceBundle
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class PropertyDictionary implements Dictionary {
|
||||
|
||||
/** Logger for {@link PropertyDictionary} */
|
||||
private static final Logger LOG = LogManager.getLogger(PropertyDictionary.class);
|
||||
|
||||
/** The resource bundle containing locale-specific translations. */
|
||||
private ResourceBundle bundle;
|
||||
|
||||
/** The locale of this dictionary. */
|
||||
private Locale locale;
|
||||
|
||||
/** The dictionary file paths. */
|
||||
private String[] dictPaths;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link PropertyDictionary} and loads the
|
||||
* dictionary file for the specified locale.
|
||||
*
|
||||
* @param locale The dictionary localization.
|
||||
* @param dictionaryPath One or more paths to dictionary files.
|
||||
*
|
||||
* @throws NullPointerException If the dictionary path is {@code null}.
|
||||
*/
|
||||
public PropertyDictionary(Locale locale, String... dictionaryPath) {
|
||||
if (dictionaryPath == null) {
|
||||
throw new NullPointerException("Dictionary path must be set.");
|
||||
}
|
||||
|
||||
this.dictPaths = dictionaryPath;
|
||||
|
||||
setLocale(locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the key is mapped in this dictionary. If the
|
||||
* dictionary contains an entry for the specified key, the associated value
|
||||
* is returned, otherwise the string {@code [*]} is returned.
|
||||
*/
|
||||
@Override
|
||||
public String get(String key) throws NullPointerException {
|
||||
try {
|
||||
return bundle.getString(key);
|
||||
}
|
||||
catch (MissingResourceException e) {
|
||||
LOG.warn("Missing resource translation", e);
|
||||
return "[*]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current locale of the dictionary.
|
||||
*
|
||||
* @return the current locale.
|
||||
*/
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the locale of the dictionary. If the new locale differs from the
|
||||
* current one, the new dictionary file is loaded.
|
||||
*
|
||||
* @param locale The new locale.
|
||||
*/
|
||||
public void setLocale(Locale locale) {
|
||||
if (this.locale != null && this.locale.equals(locale)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ResourceBundle> bundles = new ArrayList<>();
|
||||
|
||||
for (String path : dictPaths) {
|
||||
ResourceBundle bundle = ResourceBundle.getBundle(path, locale);
|
||||
|
||||
if (bundle != null) {
|
||||
bundles.add(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
this.locale = locale;
|
||||
this.bundle = new AggregateBundle(bundles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String key) {
|
||||
return bundle.containsKey(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A ResourceBundle whose content is aggregated from multiple bundles.
|
||||
*/
|
||||
private static class AggregateBundle extends ResourceBundle {
|
||||
|
||||
private final Map<String, Object> contents = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Creates a new {@link AggregateBundle} with the contents of the specified
|
||||
* resource bundles.
|
||||
*
|
||||
* @param bundles A list of bundles which shall be merged into this
|
||||
* bundle.
|
||||
*/
|
||||
AggregateBundle(List<ResourceBundle> bundles) {
|
||||
for (ResourceBundle bundle : bundles) {
|
||||
Enumeration<String> keys = bundle.getKeys();
|
||||
|
||||
while (keys.hasMoreElements()) {
|
||||
String key = keys.nextElement();
|
||||
|
||||
if (!contents.containsKey(key)) {
|
||||
contents.put(key, bundle.getObject(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getKeys() {
|
||||
return new IteratorEnumeration<>(contents.keySet().iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleGetObject(String key) {
|
||||
return contents.get(key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An Enumeration implementation that wraps an Iterator.
|
||||
*
|
||||
* @param <T> The enumerated type.
|
||||
*/
|
||||
private static class IteratorEnumeration<T> implements Enumeration<T> {
|
||||
|
||||
private final Iterator<T> source;
|
||||
|
||||
/**
|
||||
* Creates a new IterationEnumeration.
|
||||
*
|
||||
* @param source The source iterator.
|
||||
*/
|
||||
IteratorEnumeration(Iterator<T> source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
return source.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T nextElement() {
|
||||
return source.next();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.beans;
|
||||
|
||||
/**
|
||||
* Boolean property implementation.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class BooleanProperty extends ObjectProperty<Boolean> {
|
||||
|
||||
/**
|
||||
* Create a {@link BooleanProperty} with the initial value set to {@code false}.
|
||||
*/
|
||||
public BooleanProperty() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link BooleanProperty} with the specified initial value.
|
||||
*
|
||||
* @param defaultValue The initial value.
|
||||
*/
|
||||
public BooleanProperty(boolean defaultValue) {
|
||||
super(defaultValue);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.beans;
|
||||
|
||||
/**
|
||||
* A {@link ChangeListener} is notified whenever the value of an {@link Observable} has
|
||||
* changed.
|
||||
*
|
||||
* @param <T> The type of the observed data.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ChangeListener<T> {
|
||||
|
||||
/**
|
||||
* This method is called when the value of an {@link Observable} has changed.
|
||||
*
|
||||
* @param observable The {@link Observable} of which the value has changed.
|
||||
* @param oldValue The old value.
|
||||
* @param newValue The new value.
|
||||
*/
|
||||
void changed(Observable<? extends T> observable, T oldValue, T newValue);
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.beans;
|
||||
|
||||
/**
|
||||
* Interface for converting objects between type {@link S} and type {@link T}.
|
||||
*
|
||||
* @param <S> first type
|
||||
* @param <T> second type
|
||||
*/
|
||||
public interface Converter<S, T> {
|
||||
|
||||
/**
|
||||
* Converts {@code value} from type {@link S} to type {@link T}.
|
||||
*
|
||||
* @param value The value with type {@link S} to be converted.
|
||||
* @return The value with type {@link T}.
|
||||
*/
|
||||
T to(S value);
|
||||
|
||||
/**
|
||||
* Converts {@code value} from type {@link T} to type {@link S}.
|
||||
*
|
||||
* @param value The value with type {@link T} to be converted.
|
||||
* @return The value with type {@link S}.
|
||||
*/
|
||||
S from(T value);
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.beans;
|
||||
|
||||
/**
|
||||
* Double property implementation.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class DoubleProperty extends ObjectProperty<Double> {
|
||||
|
||||
/**
|
||||
* Create a {@link DoubleProperty} with the initial value set to {@code 0}.
|
||||
*/
|
||||
public DoubleProperty() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link DoubleProperty} with the specified initial value.
|
||||
*
|
||||
* @param defaultValue The initial value.
|
||||
*/
|
||||
public DoubleProperty(double defaultValue) {
|
||||
super(defaultValue);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.beans;
|
||||
|
||||
/**
|
||||
* Object property implementation.
|
||||
*
|
||||
* @param <T> The type of the object value.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class ObjectProperty<T> extends ObservableBase<T> implements Property<T> {
|
||||
|
||||
/** The object value. */
|
||||
private T value;
|
||||
|
||||
|
||||
/**
|
||||
* Create an {@link ObjectProperty} with the initial value set to {@code null}.
|
||||
*/
|
||||
public ObjectProperty() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ObjectProperty} with the specified initial value.
|
||||
*
|
||||
* @param value The initial value.
|
||||
*/
|
||||
public ObjectProperty(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T newValue) {
|
||||
if (value != newValue) {
|
||||
final T oldValue = value;
|
||||
value = newValue;
|
||||
|
||||
fireChange(this, oldValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.beans;
|
||||
|
||||
/**
|
||||
* An {@link Observable} allows to observe arbitrary content types for changes. Each
|
||||
* time the observed value changes the registered listeners will be notified.
|
||||
*
|
||||
* @param <T> The type of the observed data.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public interface Observable<T> {
|
||||
|
||||
/**
|
||||
* Adds the given {@link moodle.sync.core.beans.ChangeListener} to be notified whenever the value of this
|
||||
* {@link Observable} has changed.
|
||||
*
|
||||
* @param listener The listener to register.
|
||||
*
|
||||
* @throws NullPointerException If the listener is null.
|
||||
*/
|
||||
void addListener(ChangeListener<? super T> listener);
|
||||
|
||||
/**
|
||||
* Removes the given {@link moodle.sync.core.beans.ChangeListener} from the listener list.
|
||||
*
|
||||
* @param listener The listener to remove.
|
||||
*/
|
||||
void removeListener(ChangeListener<? super T> listener);
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.beans;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Observable base implementation that manages the change listeners and event
|
||||
* notifications.
|
||||
*
|
||||
* @param <T> The type of the observed data.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public abstract class ObservableBase<T> implements Observable<T> {
|
||||
|
||||
/** The registered change listeners. */
|
||||
private final List<ChangeListener<? super T>> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
|
||||
@Override
|
||||
public void addListener(ChangeListener<? super T> listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(ChangeListener<? super T> listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the change listeners that the observed value has changed.
|
||||
*
|
||||
* @param observable The {@link Observable} that caused the event.
|
||||
* @param oldValue The old value.
|
||||
* @param newValue The new value.
|
||||
*/
|
||||
protected void fireChange(Observable<T> observable, T oldValue, T newValue) {
|
||||
for (ChangeListener<? super T> listener : listeners) {
|
||||
listener.changed(observable, oldValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.beans;
|
||||
|
||||
/**
|
||||
* Common interface to provide a consistent mechanism for observable
|
||||
* properties.
|
||||
*
|
||||
* @param <T> The type of the data the property holds.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public interface Property<T> {
|
||||
|
||||
/**
|
||||
* Returns the current value of this {@link Property}.
|
||||
*
|
||||
* @return The current value.
|
||||
*/
|
||||
T get();
|
||||
|
||||
/**
|
||||
* Set the new value.
|
||||
*
|
||||
* @param value The new value to set.
|
||||
*/
|
||||
void set(T value);
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.beans;
|
||||
|
||||
/**
|
||||
* String property implementation.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class StringProperty extends ObjectProperty<String> {
|
||||
|
||||
/**
|
||||
* Create a {@link StringProperty} with the initial value set to {@code null}.
|
||||
*/
|
||||
public StringProperty() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link StringProperty} with the specified initial value.
|
||||
*
|
||||
* @param defaultValue The initial value.
|
||||
*/
|
||||
public StringProperty(String defaultValue) {
|
||||
super(defaultValue);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.bus;
|
||||
|
||||
/**
|
||||
* The {@link ApplicationBus} implements the publish-subscribe paradigm. It dispatches
|
||||
* application-related events to the subscribers.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class ApplicationBus {
|
||||
|
||||
/** The singleton instance. */
|
||||
private static ApplicationBus INSTANCE = null;
|
||||
|
||||
/** The event bus instance. */
|
||||
private final EventBus bus = new EventBus();
|
||||
|
||||
|
||||
/**
|
||||
* Meant to be private as for singleton purposes.
|
||||
*/
|
||||
private ApplicationBus() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ApplicationBus} singleton instance.
|
||||
*
|
||||
* @return The {@link ApplicationBus} instance.
|
||||
*/
|
||||
public static ApplicationBus getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new ApplicationBus();
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all subscriber methods on the subscriber to receive application events.
|
||||
*
|
||||
* @param subscriber The subscriber to register.
|
||||
*/
|
||||
public static void register(final Object subscriber) {
|
||||
getInstance().bus.register(subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all subscriber methods on the registered subscriber.
|
||||
*
|
||||
* @param subscriber The subscriber to unregister.
|
||||
*/
|
||||
public static void unregister(final Object subscriber) {
|
||||
getInstance().bus.unregister(subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish an application event to all registered subscribers.
|
||||
*
|
||||
* @param event The event to publish.
|
||||
*/
|
||||
public static void post(Object event) {
|
||||
getInstance().bus.post(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link EventBus} instance used by this {@link ApplicationBus}.
|
||||
*
|
||||
* @return the {@link EventBus} instance.
|
||||
*/
|
||||
public static EventBus get() {
|
||||
return getInstance().bus;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.bus;
|
||||
|
||||
import moodle.sync.core.bus.event.BusEvent;
|
||||
/**
|
||||
* The AudioBus implements the publish-subscribe paradigm. It dispatches audio
|
||||
* events to the subscribers.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class AudioBus {
|
||||
|
||||
/** The singleton instance. */
|
||||
private static AudioBus instance = null;
|
||||
|
||||
/** The event bus instance. */
|
||||
private final EventBus bus = new EventBus();
|
||||
|
||||
|
||||
/**
|
||||
* Meant to be private as for singleton purposes.
|
||||
*/
|
||||
private AudioBus() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link AudioBus} singleton instance.
|
||||
*
|
||||
* @return the {@link AudioBus} instance.
|
||||
*/
|
||||
private static AudioBus getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new AudioBus();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all subscriber methods on the subscriber to receive audio
|
||||
* events.
|
||||
*
|
||||
* @param subscriber The subscriber to register.
|
||||
*/
|
||||
public static void register(final Object subscriber) {
|
||||
getInstance().bus.register(subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all subscriber methods on the registered subscriber.
|
||||
*
|
||||
* @param subscriber The subscriber to unregister.
|
||||
*/
|
||||
public static void unregister(final Object subscriber) {
|
||||
getInstance().bus.unregister(subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish an audio event to all registered subscribers.
|
||||
*
|
||||
* @param event The event to publish.
|
||||
*/
|
||||
public static void post(final BusEvent event) {
|
||||
getInstance().bus.post(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link EventBus} instance used by this {@link AudioBus}.
|
||||
*
|
||||
* @return the {@link EventBus} instance.
|
||||
*/
|
||||
public static EventBus get() {
|
||||
return getInstance().bus;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.bus;
|
||||
|
||||
import com.google.common.eventbus.SubscriberExceptionHandler;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* The {@link EventBus} implements the publish-subscribe paradigm. It dispatches any
|
||||
* kind of events to the subscribers.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class EventBus {
|
||||
|
||||
/** Logger for {@link EventBus} */
|
||||
private static final Logger LOG = LogManager.getLogger(EventBus.class);
|
||||
|
||||
/** The handler for subscriber exceptions. */
|
||||
private final SubscriberExceptionHandler exceptionHandler = (exception, context) -> {
|
||||
if (LOG.isErrorEnabled()) {
|
||||
LOG.error("Could not dispatch event to " + context.getSubscriberMethod(), exception);
|
||||
}
|
||||
};
|
||||
|
||||
/** The internal event bus. */
|
||||
private final com.google.common.eventbus.EventBus bus;
|
||||
|
||||
|
||||
/**
|
||||
* Create an {@link EventBus}.
|
||||
*/
|
||||
public EventBus() {
|
||||
bus = new com.google.common.eventbus.EventBus(exceptionHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all subscriber methods on the subscriber to receive events
|
||||
* published on this event bus.
|
||||
*
|
||||
* @param subscriber The subscriber to register.
|
||||
*/
|
||||
public void register(final Object subscriber) {
|
||||
bus.register(subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all subscriber methods on the registered subscriber.
|
||||
*
|
||||
* @param subscriber The subscriber to unregister.
|
||||
*/
|
||||
public void unregister(final Object subscriber) {
|
||||
bus.unregister(subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish an event to all registered subscribers.
|
||||
*
|
||||
* @param event The event to publish.
|
||||
*/
|
||||
public void post(Object event) {
|
||||
bus.post(event);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.bus.event;
|
||||
|
||||
import moodle.sync.core.bus.event.EventErrorHandler;
|
||||
import moodle.sync.core.bus.event.EventProcessedHandler;
|
||||
|
||||
public abstract class BusEvent {
|
||||
|
||||
/** The {@link EventProcessedHandler} instance. */
|
||||
private EventProcessedHandler processedHandler;
|
||||
|
||||
/** The {@link EventErrorHandler} instance. */
|
||||
private EventErrorHandler errorHandler;
|
||||
|
||||
|
||||
/**
|
||||
* Get the {@link #processedHandler}.
|
||||
*
|
||||
* @return The {@link #processedHandler}.
|
||||
*/
|
||||
public EventProcessedHandler getEventProcessedHandler() {
|
||||
return processedHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new {@link #processedHandler}.
|
||||
*
|
||||
* @param handler the new {@link EventProcessedHandler}.
|
||||
*/
|
||||
public void setEventProcessedHandler(EventProcessedHandler handler) {
|
||||
this.processedHandler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link #errorHandler}.
|
||||
*
|
||||
* @return The {@link #errorHandler}.
|
||||
*/
|
||||
public EventErrorHandler getEventErrorHandler() {
|
||||
return errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new {@link #errorHandler}.
|
||||
*
|
||||
* @param errorHandler the new {@link EventErrorHandler}.
|
||||
*/
|
||||
public void setEventErrorHandler(EventErrorHandler errorHandler) {
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.bus.event;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface EventErrorHandler {
|
||||
|
||||
/**
|
||||
* Called when an error occurred while processing an event.
|
||||
*
|
||||
* @param message The error message.
|
||||
*/
|
||||
void onEventError(String message);
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.bus.event;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface EventProcessedHandler {
|
||||
|
||||
/**
|
||||
* Called when an event has been successfully processed.
|
||||
*/
|
||||
void onEventProcessed();
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.bus.event;
|
||||
|
||||
import moodle.sync.core.view.View;
|
||||
|
||||
public class ViewVisibleEvent {
|
||||
|
||||
/** The view class. */
|
||||
private final Class<? extends View> viewClass;
|
||||
|
||||
/** The visible truth value. */
|
||||
private final boolean visible;
|
||||
|
||||
|
||||
/**
|
||||
* Create the {@link ViewVisibleEvent} with specified view class and visible truth value.
|
||||
*
|
||||
* @param viewClass The view class.
|
||||
* @param visible The visible truth value.
|
||||
*/
|
||||
public ViewVisibleEvent(Class<? extends View> viewClass, boolean visible) {
|
||||
this.viewClass = viewClass;
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view class.
|
||||
*
|
||||
* @return The view class.
|
||||
*/
|
||||
public Class<? extends View> getViewClass() {
|
||||
return viewClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visible truth value.
|
||||
*
|
||||
* @return The visible truth value.
|
||||
*/
|
||||
public boolean isVisible() {
|
||||
return visible;
|
||||
}
|
||||
}
|
|
@ -2,11 +2,11 @@ package moodle.sync.core.config;
|
|||
|
||||
|
||||
import moodle.sync.core.model.json.Course;
|
||||
import org.lecturestudio.core.beans.BooleanProperty;
|
||||
import moodle.sync.core.beans.BooleanProperty;
|
||||
import moodle.sync.core.model.json.Section;
|
||||
import org.lecturestudio.core.app.configuration.Configuration;
|
||||
import org.lecturestudio.core.beans.ObjectProperty;
|
||||
import org.lecturestudio.core.beans.StringProperty;
|
||||
import moodle.sync.core.app.configuration.Configuration;
|
||||
import moodle.sync.core.beans.ObjectProperty;
|
||||
import moodle.sync.core.beans.StringProperty;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
|
|
@ -4,13 +4,13 @@ import java.io.File;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.lecturestudio.core.app.AppDataLocator;
|
||||
import org.lecturestudio.core.app.ApplicationContext;
|
||||
import org.lecturestudio.core.app.configuration.Configuration;
|
||||
import org.lecturestudio.core.app.configuration.ConfigurationService;
|
||||
import org.lecturestudio.core.app.configuration.JsonConfigurationService;
|
||||
import org.lecturestudio.core.app.dictionary.Dictionary;
|
||||
import org.lecturestudio.core.bus.EventBus;
|
||||
import moodle.sync.core.app.AppDataLocator;
|
||||
import moodle.sync.core.app.ApplicationContext;
|
||||
import moodle.sync.core.app.configuration.Configuration;
|
||||
import moodle.sync.core.app.configuration.ConfigurationService;
|
||||
import moodle.sync.core.app.configuration.JsonConfigurationService;
|
||||
import moodle.sync.core.app.dictionary.Dictionary;
|
||||
import moodle.sync.core.bus.EventBus;
|
||||
|
||||
public class MoodleSyncContext extends ApplicationContext {
|
||||
|
||||
|
@ -19,8 +19,8 @@ public class MoodleSyncContext extends ApplicationContext {
|
|||
|
||||
@Inject
|
||||
public MoodleSyncContext(AppDataLocator dataLocator, File configFile,
|
||||
Configuration config, Dictionary dict, EventBus eventBus,
|
||||
EventBus audioBus) {
|
||||
Configuration config, Dictionary dict, EventBus eventBus,
|
||||
EventBus audioBus) {
|
||||
super(dataLocator, config, dict, eventBus, audioBus);
|
||||
|
||||
this.configFile = configFile;
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.geometry;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* The {@link Dimension2D} represents the horizontal and vertical extent of an object in 2D space.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class Dimension2D implements Cloneable, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 221175148276014654L;
|
||||
|
||||
/** The width. */
|
||||
private double width;
|
||||
|
||||
/** The height. */
|
||||
private double height;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link Dimension2D} with zero size.
|
||||
*/
|
||||
public Dimension2D() {
|
||||
this(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link Dimension2D} with the specified size.
|
||||
*
|
||||
* @param width The width.
|
||||
* @param height The height.
|
||||
*/
|
||||
public Dimension2D(double width, double height) {
|
||||
setSize(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* The width of this dimension.
|
||||
*
|
||||
* @return The width.
|
||||
*/
|
||||
public double getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* The height of this dimension.
|
||||
*
|
||||
* @return The height.
|
||||
*/
|
||||
public double getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new width and height of this dimension.
|
||||
*
|
||||
* @param width The new width to set.
|
||||
* @param height The new height to set.
|
||||
*/
|
||||
public void setSize(double width, double height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Dimension2D other = (Dimension2D) obj;
|
||||
|
||||
return Objects.equal(getWidth(), other.getWidth()) && Objects
|
||||
.equal(getHeight(), other.getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + " (" + width + ", " + height + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension2D clone() {
|
||||
try {
|
||||
return (Dimension2D) super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.geometry;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A geometric point that represents the x, y coordinates.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class Point2D implements Cloneable, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 3737399850497337585L;
|
||||
|
||||
/** The x coordinate. */
|
||||
private double x;
|
||||
|
||||
/** The y coordinate. */
|
||||
private double y;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance of {@link Point2D} with origin coordinates.
|
||||
*/
|
||||
public Point2D() {
|
||||
this(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of {@link Point2D} with coordinates from provided point.
|
||||
*/
|
||||
public Point2D(Point2D point) {
|
||||
this(point.getX(), point.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link Point2D} with specified coordinates.
|
||||
*
|
||||
* @param x The x coordinate of the point.
|
||||
* @param y The y coordinate of the point.
|
||||
*/
|
||||
public Point2D(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* The x coordinate of the point.
|
||||
*
|
||||
* @return The x coordinate.
|
||||
*/
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* The y coordinate of the point.
|
||||
*
|
||||
* @return The y coordinate.
|
||||
*/
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new coordinates of this point.
|
||||
*
|
||||
* @param x The x coordinate.
|
||||
* @param y The y coordinate.
|
||||
*/
|
||||
public <T extends Point2D> T set(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the coordinates to the values from the specified point.
|
||||
*
|
||||
* @param p The point from which to copy the coordinates.
|
||||
*
|
||||
* @return This point.
|
||||
*/
|
||||
public <T extends Point2D> T set(Point2D p) {
|
||||
set(p.x, p.y);
|
||||
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance from this point to a specified point.
|
||||
*
|
||||
* @param p The point to which the distance should be measured.
|
||||
*
|
||||
* @return The distance to the given point.
|
||||
*/
|
||||
public double distance(Point2D p) {
|
||||
double dx = p.x - x;
|
||||
double dy = p.y - y;
|
||||
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the dot product of the vector represented by this instance and
|
||||
* the specified vector.
|
||||
*
|
||||
* @param p The other vector.
|
||||
*
|
||||
* @return The dot product of the two vectors
|
||||
*/
|
||||
public double dot(Point2D p) {
|
||||
return x * p.x + y * p.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add coordinates of the specified point to the coordinates of this point.
|
||||
*
|
||||
* @param p The point whose coordinates are to be added.
|
||||
*
|
||||
* @return This point instance with added coordinates.
|
||||
*/
|
||||
public <T extends Point2D> T add(Point2D p) {
|
||||
x += p.x;
|
||||
y += p.y;
|
||||
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract coordinates of the specified point from the coordinates of this point.
|
||||
*
|
||||
* @param p The point whose coordinates are to be subtracted.
|
||||
*
|
||||
* @return This point instance with subtracted coordinates.
|
||||
*/
|
||||
public <T extends Point2D> T subtract(Point2D p) {
|
||||
x -= p.x;
|
||||
y -= p.y;
|
||||
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolate a point between the specified point and this point with the defined scalar.
|
||||
*
|
||||
* @param v The other point that will define a line segment with this point.
|
||||
* @param f The scalar.
|
||||
*
|
||||
* @return An interpolated point.
|
||||
*/
|
||||
public Point2D interpolate(Point2D v, double f) {
|
||||
return new Point2D(this.x + (v.x - this.x) * f, this.y + (v.y - this.y) * f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply the coordinates of this point with the specified factor.
|
||||
*
|
||||
* @param factor The multiplying factor.
|
||||
*
|
||||
* @return This point instance with multiplied coordinates.
|
||||
*/
|
||||
public <T extends Point2D> T multiply(double factor) {
|
||||
x *= factor;
|
||||
y *= factor;
|
||||
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the relative magnitude vector represented by this point.
|
||||
*
|
||||
* @return This point instance as the normalized vector.
|
||||
*/
|
||||
public <T extends Point2D> T normalize() {
|
||||
double length = Math.sqrt(x * x + y * y);
|
||||
|
||||
x /= length;
|
||||
y /= length;
|
||||
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize this point to the specified length.
|
||||
*
|
||||
* @param length The length to normalize to.
|
||||
*
|
||||
* @return This normalized point instance.
|
||||
*/
|
||||
public <T extends Point2D> T normalize(double length) {
|
||||
double mag = Math.sqrt(x * x + y * y);
|
||||
|
||||
if (mag > 0) {
|
||||
mag = length / mag;
|
||||
x *= mag;
|
||||
y *= mag;
|
||||
}
|
||||
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a point on the perpendicular of this point.
|
||||
*
|
||||
* @return A new point on the perpendicular of this point.
|
||||
*/
|
||||
public <T extends Point2D> T perpendicular() {
|
||||
double t = x;
|
||||
this.x = -y;
|
||||
this.y = t;
|
||||
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + " (" + x + ", " + y + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(x);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(y);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Point2D other = (Point2D) obj;
|
||||
|
||||
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Double.doubleToLongBits(y) == Double.doubleToLongBits(other.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D clone() {
|
||||
return new Point2D(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverts the coordinates
|
||||
*
|
||||
* @return itself with the changed coordinates
|
||||
*/
|
||||
public <T extends Point2D> T invert() {
|
||||
x = -x;
|
||||
y = -y;
|
||||
|
||||
return (T) this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.geometry;
|
||||
|
||||
/**
|
||||
* A set of values for describing vertical and horizontal positioning.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public enum Position {
|
||||
|
||||
/** Positioning on the top vertically and on the left horizontally. */
|
||||
TOP_LEFT,
|
||||
|
||||
/** Positioning on the top vertically and on the center horizontally. */
|
||||
TOP_CENTER,
|
||||
|
||||
/** Positioning on the top vertically and on the right horizontally. */
|
||||
TOP_RIGHT,
|
||||
|
||||
/** Positioning on the center vertically and on the left horizontally. */
|
||||
CENTER_LEFT,
|
||||
|
||||
/** Positioning on the center both vertically and horizontally. */
|
||||
CENTER,
|
||||
|
||||
/** Positioning on the center vertically and on the right horizontally. */
|
||||
CENTER_RIGHT,
|
||||
|
||||
/** Positioning on the bottom vertically and on the left horizontally. */
|
||||
BOTTOM_LEFT,
|
||||
|
||||
/** Positioning on the bottom vertically and on the center horizontally. */
|
||||
BOTTOM_CENTER,
|
||||
|
||||
/** Positioning on the bottom vertically and on the right horizontally. */
|
||||
BOTTOM_RIGHT
|
||||
|
||||
}
|
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.geometry;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A {@link Rectangle2D} represents a rectangle in 2D space defined by a location (x, y) and dimension (w x h).
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class Rectangle2D implements Cloneable, Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3647938113618642582L;
|
||||
|
||||
/** The x coordinate. */
|
||||
private double x;
|
||||
|
||||
/** The y coordinate. */
|
||||
private double y;
|
||||
|
||||
/** The width. */
|
||||
private double width;
|
||||
|
||||
/** The height. */
|
||||
private double height;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link Rectangle2D} with origin coordinates and zero size.
|
||||
*/
|
||||
public Rectangle2D() {
|
||||
this(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link Rectangle2D} with specified location coordinates and size.
|
||||
*
|
||||
* @param x The x coordinate of the rectangle.
|
||||
* @param y The y coordinate of the rectangle.
|
||||
* @param width The width of the rectangle.
|
||||
* @param height The height of the rectangle.
|
||||
*/
|
||||
public Rectangle2D(double x, double y, double width, double height) {
|
||||
setRect(x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link Rectangle2D} with specified {@link Rectangle2D} to copy.
|
||||
*
|
||||
* @param rect The rectangle to copy.
|
||||
*/
|
||||
public Rectangle2D(Rectangle2D rect) {
|
||||
setRect(rect.x, rect.y, rect.width, rect.height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location of the <code>Rectangle2D</code> to the specified values.
|
||||
*
|
||||
* @param x The new x coordinate.
|
||||
* @param y The new y coordinate.
|
||||
*/
|
||||
public void setLocation(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of the {@link Rectangle2D}> to the specified values.
|
||||
*
|
||||
* @param width The width.
|
||||
* @param height The height.
|
||||
*/
|
||||
public void setSize(double width, double height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* The location of the {@link Rectangle2D} represented by {@link Point2D}.
|
||||
*
|
||||
* @return The location of this rectangle.
|
||||
*/
|
||||
public Point2D getLocation() {
|
||||
return new Point2D(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* The x coordinate of the top left corner.
|
||||
*
|
||||
* @return The x coordinate.
|
||||
*/
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* The y coordinate of the top left corner.
|
||||
*
|
||||
* @return The y coordinate.
|
||||
*/
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* The width of the {@link Rectangle2D}.
|
||||
*
|
||||
* @return The width of this rectangle.
|
||||
*/
|
||||
public double getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* The height of the {@link Rectangle2D}.
|
||||
*
|
||||
* @return The height of this rectangle.
|
||||
*/
|
||||
public double getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a point to the {@link Rectangle2D}. The resulting {@link Rectangle2D} is enlarged
|
||||
* so it contains the specified point.
|
||||
*
|
||||
* @param x The x coordinate of the point.
|
||||
* @param y The y coordinate of the point.
|
||||
*/
|
||||
public void add(double x, double y) {
|
||||
double x1 = Math.min(this.x, x);
|
||||
double x2 = Math.max(this.x + this.width, x);
|
||||
double y1 = Math.min(this.y, y);
|
||||
double y2 = Math.max(this.y + this.height, y);
|
||||
|
||||
setRect(x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks, if the specified {@link Point2D} is inside the boundary of the {@link Rectangle2D}.
|
||||
*
|
||||
* @param point The {@link Point2D} that represents a x and y coordinate pair.
|
||||
*
|
||||
* @return {@code true} if the specified {@link Point2D} is inside the boundary, otherwise {@code true}.
|
||||
*/
|
||||
public boolean contains(Point2D point) {
|
||||
double px = point.getX();
|
||||
double py = point.getY();
|
||||
|
||||
return (px >= x && py >= y && px < x + getWidth() && py < y + getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the interior of this {@link Rectangle2D} entirely encloses the specified {@link Rectangle2D}.
|
||||
*
|
||||
* @param r The {@link Rectangle2D} to check if it is enclosed by this {@link Rectangle2D}.
|
||||
*
|
||||
* @return {@code true} if the interior of this {@link Rectangle2D} entirely contains the specified area,
|
||||
* otherwise {@code false}.
|
||||
*/
|
||||
public boolean contains(Rectangle2D r) {
|
||||
double w = r.getWidth();
|
||||
double h = r.getHeight();
|
||||
|
||||
if (isEmpty() || w <= 0 || h <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double x = r.getX();
|
||||
double y = r.getY();
|
||||
double x0 = getX();
|
||||
double y0 = getY();
|
||||
|
||||
return (x >= x0 && y >= y0 && (x + w) <= x0 + getWidth()
|
||||
&& (y + h) <= y0 + getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersects the provided {@link Rectangle2D} with this one and puts the result into
|
||||
* the returned {@link Rectangle2D} object.
|
||||
*
|
||||
* @param rect The {@link Rectangle2D} to be intersected with this one.
|
||||
*
|
||||
* @return The intersection rectangle, or {@code null} if the rectangles don't intersect each other.
|
||||
*/
|
||||
public Rectangle2D intersection(Rectangle2D rect) {
|
||||
double iX = Math.max(x, rect.x);
|
||||
double iY = Math.max(y, rect.y);
|
||||
double iW = Math.min(x + width, rect.x + rect.width) - iX;
|
||||
double iH = Math.min(y + height, rect.y + rect.height) - iY;
|
||||
|
||||
long zero = Double.doubleToLongBits(0);
|
||||
|
||||
if (Double.doubleToLongBits(iW) <= zero) {
|
||||
return null;
|
||||
}
|
||||
if (Double.doubleToLongBits(iH) <= zero) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Rectangle2D(iX, iY, iW, iH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the {@link Rectangle2D} encloses some area.
|
||||
*
|
||||
* @return {@code true} if the {@link Rectangle2D} is empty, otherwise {@code false}.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return (width <= 0.0) || (height <= 0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the location and size of the {@link Rectangle2D} to the specified values.
|
||||
*
|
||||
* @param x The x coordinate.
|
||||
* @param y The y coordinate.
|
||||
* @param width The width.
|
||||
* @param height The height.
|
||||
*/
|
||||
public void setRect(double x, double y, double width, double height) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the diagonal of this rectangle.
|
||||
*
|
||||
* @param x1 the x coordinate of the start point of the diagonal.
|
||||
* @param y1 the y coordinate of the start point of the diagonal.
|
||||
* @param x2 the x coordinate of the end point of the diagonal.
|
||||
* @param y2 the y coordinate of the end point of the diagonal.
|
||||
*/
|
||||
public void setFromDiagonal(double x1, double y1, double x2, double y2) {
|
||||
if (x2 < x1) {
|
||||
double t = x1;
|
||||
x1 = x2;
|
||||
x2 = t;
|
||||
}
|
||||
if (y2 < y1) {
|
||||
double t = y1;
|
||||
y1 = y2;
|
||||
y2 = t;
|
||||
}
|
||||
|
||||
setLocation(x1, y1);
|
||||
setSize(x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unions the provided {@link Rectangle2D} with this one and puts the result into this {@link Rectangle2D} object.
|
||||
*
|
||||
* @param rect The {@link Rectangle2D} to be combined with this one.
|
||||
*/
|
||||
public void union(Rectangle2D rect) {
|
||||
double x1 = Math.min(getX(), rect.getX());
|
||||
double y1 = Math.min(getY(), rect.getY());
|
||||
double x2 = Math.max(getX() + getWidth(), rect.getX() + rect.getWidth());
|
||||
double y2 = Math.max(getY() + getHeight(), rect.getY() + rect.getHeight());
|
||||
|
||||
setRect(x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + " (" + x + ", " + y + ", " + width + ", " + height + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
long temp;
|
||||
|
||||
temp = Double.doubleToLongBits(height);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(width);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(x);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(y);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Rectangle2D other = (Rectangle2D) obj;
|
||||
|
||||
if (Double.doubleToLongBits(height) != Double.doubleToLongBits(other.height)) {
|
||||
return false;
|
||||
}
|
||||
if (Double.doubleToLongBits(width) != Double.doubleToLongBits(other.width)) {
|
||||
return false;
|
||||
}
|
||||
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Double.doubleToLongBits(y) == Double.doubleToLongBits(other.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D clone() {
|
||||
return new Rectangle2D(x, y, width, height);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.graphics;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Color implements Cloneable, Serializable {
|
||||
|
||||
private static final long serialVersionUID = -2703233901325908526L;
|
||||
|
||||
/** The color white in the default sRGB space. */
|
||||
public final static Color WHITE = new Color(255, 255, 255);
|
||||
|
||||
/** The color black in the default sRGB space. */
|
||||
public final static Color BLACK = new Color(0, 0, 0);
|
||||
|
||||
/** The color value containing RGBA components. */
|
||||
private final int value;
|
||||
|
||||
|
||||
/**
|
||||
* Copy-constructor.
|
||||
*
|
||||
* @param color The color to copy.
|
||||
*/
|
||||
public Color(Color color) {
|
||||
this(color.getRGBA());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link Color}. The values must be in the range 0 - 255.
|
||||
*
|
||||
* @param red The red component of the {@link Color}.
|
||||
* @param green The green component of the {@link Color}.
|
||||
* @param blue The blue component of the {@link Color}.
|
||||
*/
|
||||
public Color(int red, int green, int blue) {
|
||||
this(red, green, blue, 255);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link Color}. The values must be in the range 0 - 255.
|
||||
*
|
||||
* @param red The red component of the {@link Color}.
|
||||
* @param green The green component of the {@link Color}.
|
||||
* @param blue The blue component of the {@link Color}.
|
||||
* @param opacity The opacity of the {@link Color}.
|
||||
*/
|
||||
public Color(int red, int green, int blue, int opacity) {
|
||||
value = ((opacity & 0xFF) << 24) |
|
||||
((red & 0xFF) << 16) |
|
||||
((green & 0xFF) << 8) |
|
||||
((blue & 0xFF));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link Color} with the specified combined RGBA value
|
||||
* consisting of the alpha component in bits 24-31, the red component in bits 16-23,
|
||||
* the green component in bits 8-15, and the blue component in bits 0-7.
|
||||
*
|
||||
* @param rgba The combined RGBA components.
|
||||
*/
|
||||
public Color(int rgba) {
|
||||
this.value = rgba;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Color} with the specified opacity.
|
||||
*
|
||||
* @param opacity The new opacity of the {@link Color}.
|
||||
*
|
||||
* @return A new {@link Color} with the given opacity.
|
||||
*/
|
||||
public Color derive(int opacity) {
|
||||
return new Color(((opacity & 0xFF) << 24) | (value & 0x00FFFFFF));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates an interpolated {@link Color} along the fraction between {@code 0.0}
|
||||
* and {@code 1.0}. If {@code fraction == 1.0}, {@code endColor} is returned.
|
||||
*
|
||||
* @param endColor The color the interpolation ends with.
|
||||
* @param fraction The fraction between {@code 0.0} and {@code 1.0}
|
||||
*
|
||||
* @return The interpolated {@link Color}.
|
||||
*/
|
||||
public Color interpolate(Color endColor, double fraction) {
|
||||
if (fraction <= 0.0) {
|
||||
return this;
|
||||
}
|
||||
if (fraction >= 1.0) {
|
||||
return endColor;
|
||||
}
|
||||
|
||||
int r = getRed();
|
||||
int g = getGreen();
|
||||
int b = getBlue();
|
||||
int a = getOpacity();
|
||||
|
||||
return new Color(
|
||||
(int) (r + (endColor.getRed() - r) * fraction),
|
||||
(int) (g + (endColor.getGreen() - g) * fraction),
|
||||
(int) (b + (endColor.getBlue() - b) * fraction),
|
||||
(int) (a + (endColor.getOpacity() - a) * fraction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the RGBA value representing the color.
|
||||
*
|
||||
* @return The RGBA value of the color.
|
||||
*/
|
||||
public int getRGBA() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The red component of the {@link Color}, in the range 0 - 255.
|
||||
*
|
||||
* @return The red component.
|
||||
*/
|
||||
public int getRed() {
|
||||
return (getRGBA() >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* The green component of the {@link Color}, in the range 0 - 255.
|
||||
*
|
||||
* @return The green component.
|
||||
*/
|
||||
public int getGreen() {
|
||||
return (getRGBA() >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* The blue component of the {@link Color}, in the range 0 - 255.
|
||||
*
|
||||
* @return The blue component.
|
||||
*/
|
||||
public int getBlue() {
|
||||
return getRGBA() & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* The opacity of the {@link Color}, in the range 0 - 255.
|
||||
*
|
||||
* @return The opacity.
|
||||
*/
|
||||
public int getOpacity() {
|
||||
return (getRGBA() >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the opacity equals {@code 255}.
|
||||
*
|
||||
* @return {@code true} if the opacity equals {@code 255}, otherwise {@code false}.
|
||||
*/
|
||||
public boolean isOpaque() {
|
||||
return getOpacity() == 255;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Color other = (Color) obj;
|
||||
|
||||
return this.value == other.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Color (" + getRed() + " " + getGreen() + " " + getBlue() + " " + getOpacity() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color clone() {
|
||||
try {
|
||||
return (Color) super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.inject;
|
||||
|
||||
import moodle.sync.core.view.DirectoryChooserView;
|
||||
import moodle.sync.core.view.FileChooserView;
|
||||
import moodle.sync.core.view.ViewContextFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
||||
public class DIViewContextFactory implements ViewContextFactory {
|
||||
|
||||
private final Injector injector;
|
||||
|
||||
|
||||
@Inject
|
||||
public DIViewContextFactory(Injector injector) {
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getInstance(Class<T> cls) {
|
||||
return injector.getInstance(cls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileChooserView createFileChooserView() {
|
||||
return injector.getInstance(FileChooserView.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryChooserView createDirectoryChooserView() {
|
||||
return injector.getInstance(DirectoryChooserView.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.inject;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Module;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GuiceInjector implements Injector {
|
||||
|
||||
private final com.google.inject.Injector injector;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link GuiceInjector} with the specified modules.
|
||||
*
|
||||
* @param modules The modules.
|
||||
*/
|
||||
public GuiceInjector(Module... modules) {
|
||||
List<Module> list = new ArrayList<>(List.of(modules));
|
||||
list.add(binder -> binder.bind(Injector.class).toInstance(this));
|
||||
|
||||
injector = Guice.createInjector(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getInstance(Class<T> cls) {
|
||||
return injector.getInstance(cls);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.inject;
|
||||
|
||||
/**
|
||||
* Common definition of a Dependency Injection Context.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public interface Injector {
|
||||
|
||||
/**
|
||||
* Get an instance of the given class.
|
||||
*
|
||||
* @param cls The class of the instance.
|
||||
* @param <T> The resulting instance type.
|
||||
*
|
||||
* @return The resulting instance.
|
||||
*/
|
||||
<T> T getInstance(Class<T> cls);
|
||||
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.input;
|
||||
|
||||
/**
|
||||
* Enum with key code names the underlying platform codes used to represent the characters.
|
||||
*/
|
||||
public enum KeyCode {
|
||||
|
||||
ENTER(0x0A, "Enter"),
|
||||
BACK_SPACE(0x08, "Backspace"),
|
||||
TAB(0x09, "Tab"),
|
||||
CANCEL(0x03, "Cancel"),
|
||||
CLEAR(0x0C, "Clear"),
|
||||
SHIFT(0x10, "Shift"),
|
||||
CONTROL(0x11, "Ctrl"),
|
||||
ALT(0x12, "Alt"),
|
||||
PAUSE(0x13, "Pause"),
|
||||
CAPS(0x14, "Caps Lock"),
|
||||
ESCAPE(0x1B, "Esc"),
|
||||
SPACE(0x20, "Space"),
|
||||
PAGE_UP(0x21, "Page Up"),
|
||||
PAGE_DOWN(0x22, "Page Down"),
|
||||
END(0x23, "End"),
|
||||
HOME(0x24, "Home"),
|
||||
LEFT(0x25, "Left"),
|
||||
UP(0x26, "Up"),
|
||||
RIGHT(0x27, "Right"),
|
||||
DOWN(0x28, "Down"),
|
||||
COMMA(0x2C, "Comma"),
|
||||
MINUS(0x2D, "Minus"),
|
||||
PERIOD(0x2E, "Period"),
|
||||
SLASH(0x2F, "Slash"),
|
||||
DIGIT0(0x30, "0"),
|
||||
DIGIT1(0x31, "1"),
|
||||
DIGIT2(0x32, "2"),
|
||||
DIGIT3(0x33, "3"),
|
||||
DIGIT4(0x34, "4"),
|
||||
DIGIT5(0x35, "5"),
|
||||
DIGIT6(0x36, "6"),
|
||||
DIGIT7(0x37, "7"),
|
||||
DIGIT8(0x38, "8"),
|
||||
DIGIT9(0x39, "9"),
|
||||
SEMICOLON(0x3B, "Semicolon"),
|
||||
EQUALS(0x3D, "Equals"),
|
||||
A(0x41, "A"),
|
||||
B(0x42, "B"),
|
||||
C(0x43, "C"),
|
||||
D(0x44, "D"),
|
||||
E(0x45, "E"),
|
||||
F(0x46, "F"),
|
||||
G(0x47, "G"),
|
||||
H(0x48, "H"),
|
||||
I(0x49, "I"),
|
||||
J(0x4A, "J"),
|
||||
K(0x4B, "K"),
|
||||
L(0x4C, "L"),
|
||||
M(0x4D, "M"),
|
||||
N(0x4E, "N"),
|
||||
O(0x4F, "O"),
|
||||
P(0x50, "P"),
|
||||
Q(0x51, "Q"),
|
||||
R(0x52, "R"),
|
||||
S(0x53, "S"),
|
||||
T(0x54, "T"),
|
||||
U(0x55, "U"),
|
||||
V(0x56, "V"),
|
||||
W(0x57, "W"),
|
||||
X(0x58, "X"),
|
||||
Y(0x59, "Y"),
|
||||
Z(0x5A, "Z"),
|
||||
OPEN_BRACKET(0x5B, "Open Bracket"),
|
||||
BACK_SLASH(0x5C, "Back Slash"),
|
||||
CLOSE_BRACKET(0x5D, "Close Bracket"),
|
||||
NUMPAD0(0x60, "Numpad 0"),
|
||||
NUMPAD1(0x61, "Numpad 1"),
|
||||
NUMPAD2(0x62, "Numpad 2"),
|
||||
NUMPAD3(0x63, "Numpad 3"),
|
||||
NUMPAD4(0x64, "Numpad 4"),
|
||||
NUMPAD5(0x65, "Numpad 5"),
|
||||
NUMPAD6(0x66, "Numpad 6"),
|
||||
NUMPAD7(0x67, "Numpad 7"),
|
||||
NUMPAD8(0x68, "Numpad 8"),
|
||||
NUMPAD9(0x69, "Numpad 9"),
|
||||
MULTIPLY(0x6A, "Multiply"),
|
||||
ADD(0x6B, "Add"),
|
||||
SEPARATOR(0x6C, "Separator"),
|
||||
SUBTRACT(0x6D, "Subtract"),
|
||||
DECIMAL(0x6E, "Decimal"),
|
||||
DIVIDE(0x6F, "Divide"),
|
||||
DELETE(0x7F, "Delete"),
|
||||
NUM_LOCK(0x90, "Num Lock"),
|
||||
SCROLL_LOCK(0x91, "Scroll Lock"),
|
||||
F1(0x70, "F1"),
|
||||
F2(0x71, "F2"),
|
||||
F3(0x72, "F3"),
|
||||
F4(0x73, "F4"),
|
||||
F5(0x74, "F5"),
|
||||
F6(0x75, "F6"),
|
||||
F7(0x76, "F7"),
|
||||
F8(0x77, "F8"),
|
||||
F9(0x78, "F9"),
|
||||
F10(0x79, "F10"),
|
||||
F11(0x7A, "F11"),
|
||||
F12(0x7B, "F12"),
|
||||
F13(0xF000, "F13"),
|
||||
F14(0xF001, "F14"),
|
||||
F15(0xF002, "F15"),
|
||||
F16(0xF003, "F16"),
|
||||
F17(0xF004, "F17"),
|
||||
F18(0xF005, "F18"),
|
||||
F19(0xF006, "F19"),
|
||||
F20(0xF007, "F20"),
|
||||
F21(0xF008, "F21"),
|
||||
F22(0xF009, "F22"),
|
||||
F23(0xF00A, "F23"),
|
||||
F24(0xF00B, "F24"),
|
||||
PRINTSCREEN(0x9A, "Print Screen"),
|
||||
INSERT(0x9B, "Insert"),
|
||||
HELP(0x9C, "Help"),
|
||||
META(0x9D, "Meta"),
|
||||
BACK_QUOTE(0xC0, "Back Quote"),
|
||||
QUOTE(0xDE, "Quote"),
|
||||
KP_UP(0xE0, "Numpad Up"),
|
||||
KP_DOWN(0xE1, "Numpad Down"),
|
||||
KP_LEFT(0xE2, "Numpad Left"),
|
||||
KP_RIGHT(0xE3, "Numpad Right"),
|
||||
AMPERSAND(0x96, "Ampersand"),
|
||||
ASTERISK(0x97, "Asterisk"),
|
||||
QUOTEDBL(0x98, "Double Quote"),
|
||||
LESS(0x99, "Less"),
|
||||
GREATER(0xa0, "Greater"),
|
||||
BRACELEFT(0xa1, "Left Brace"),
|
||||
BRACERIGHT(0xa2, "Right Brace"),
|
||||
AT(0x0200, "At"),
|
||||
COLON(0x0201, "Colon"),
|
||||
CIRCUMFLEX(0x0202, "Circumflex"),
|
||||
DOLLAR(0x0203, "Dollar"),
|
||||
EURO_SIGN(0x0204, "Euro Sign"),
|
||||
EXCLAMATION_MARK(0x0205, "Exclamation Mark"),
|
||||
INVERTED_EXCLAMATION_MARK(0x0206, "Inverted Exclamation Mark"),
|
||||
LEFT_PARENTHESIS(0x0207, "Left Parenthesis"),
|
||||
NUMBER_SIGN(0x0208, "Number Sign"),
|
||||
PLUS(0x0209, "Plus"),
|
||||
RIGHT_PARENTHESIS(0x020A, "Right Parenthesis"),
|
||||
UNDERSCORE(0x020B, "Underscore"),
|
||||
WINDOWS(0x020C, "Windows"),
|
||||
CONTEXT_MENU(0x020D, "Context Menu"),
|
||||
FINAL(0x0018, "Final"),
|
||||
CONVERT(0x001C, "Convert"),
|
||||
NONCONVERT(0x001D, "Nonconvert"),
|
||||
ACCEPT(0x001E, "Accept"),
|
||||
MODECHANGE(0x001F, "Mode Change"),
|
||||
ALPHANUMERIC(0x00F0, "Alphanumeric"),
|
||||
CUT(0xFFD1, "Cut"),
|
||||
COPY(0xFFCD, "Copy"),
|
||||
PASTE(0xFFCF, "Paste"),
|
||||
UNDO(0xFFCB, "Undo"),
|
||||
ALT_GRAPH(0xFF7E, "Alt Graph"),
|
||||
SOFTKEY_0(0x1000, "Softkey 0"),
|
||||
SOFTKEY_1(0x1001, "Softkey 1"),
|
||||
SOFTKEY_2(0x1002, "Softkey 2"),
|
||||
SOFTKEY_3(0x1003, "Softkey 3"),
|
||||
SOFTKEY_4(0x1004, "Softkey 4"),
|
||||
SOFTKEY_5(0x1005, "Softkey 5"),
|
||||
SOFTKEY_6(0x1006, "Softkey 6"),
|
||||
SOFTKEY_7(0x1007, "Softkey 7"),
|
||||
SOFTKEY_8(0x1008, "Softkey 8"),
|
||||
SOFTKEY_9(0x1009, "Softkey 9");
|
||||
|
||||
/** The platform code used to represent the character. */
|
||||
final int code;
|
||||
|
||||
/** The string representation of the {@link #code}. */
|
||||
final String ch;
|
||||
|
||||
/** The name of this key code. */
|
||||
final String name;
|
||||
|
||||
KeyCode(int code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.ch = String.valueOf((char)code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying platform code used to represent the character.
|
||||
*
|
||||
* @return the underlying platform code.
|
||||
*/
|
||||
public final int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this key code.
|
||||
*
|
||||
* @return The name of this key code.
|
||||
*/
|
||||
public final String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.input;
|
||||
|
||||
public class KeyEvent {
|
||||
|
||||
/** Enum with the types of the key event. */
|
||||
public enum EventType { PRESSED, RELEASED, TYPED }
|
||||
|
||||
/** The Shift key modifier constant. */
|
||||
public static final int SHIFT_MASK = 1 << 1;
|
||||
|
||||
/** The Control key modifier constant. */
|
||||
public static final int CTRL_MASK = 1 << 2;
|
||||
|
||||
/** The Alt key modifier constant. */
|
||||
public static final int ALT_MASK = 1 << 3;
|
||||
|
||||
/** The AltGraph key modifier constant. */
|
||||
public static final int ALT_GRAPH_MASK = 1 << 4;
|
||||
|
||||
/** The event type. */
|
||||
private final EventType eventType;
|
||||
|
||||
/** The key code. */
|
||||
private final int keyCode;
|
||||
|
||||
/** The modifiers. */
|
||||
private final int modifiers;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link KeyEvent} with the specified key code.
|
||||
* (Calls {@link #KeyEvent(int, int, EventType)} with {@code 0} as modifiers and
|
||||
* {@code EventType.PRESSED} as event type.)
|
||||
*
|
||||
* @param keyCode The key code.
|
||||
*/
|
||||
public KeyEvent(int keyCode) {
|
||||
this(keyCode, 0, EventType.PRESSED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link KeyEvent} with the specified key code and modifiers.
|
||||
* (Calls {@link #KeyEvent(int, int, EventType)} with {@code EventType.PRESSED} as event type.)
|
||||
*
|
||||
* @param keyCode The key code.
|
||||
* @param modifiers The modifiers.
|
||||
*/
|
||||
public KeyEvent(int keyCode, int modifiers) {
|
||||
this(keyCode, modifiers, EventType.PRESSED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link KeyEvent} with the specified modifiers and event type.
|
||||
* (Calls {@link #KeyEvent(int, int, EventType)} with {@code 0} as key code.)
|
||||
*
|
||||
* @param modifiers The modifiers.
|
||||
* @param eventType The event type.
|
||||
*/
|
||||
public KeyEvent(int modifiers, EventType eventType) {
|
||||
this(0, modifiers, eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link KeyEvent} with the specified key code, modifiers and event type.
|
||||
*
|
||||
* @param keyCode The key code.
|
||||
* @param modifiers The modifiers.
|
||||
* @param eventType The event type.
|
||||
*/
|
||||
public KeyEvent(int keyCode, int modifiers, EventType eventType) {
|
||||
this.keyCode = keyCode;
|
||||
this.modifiers = modifiers;
|
||||
this.eventType = eventType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key code.
|
||||
*
|
||||
* @return The key code.
|
||||
*/
|
||||
public int getKeyCode() {
|
||||
return keyCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the modifiers.
|
||||
*
|
||||
* @return The modifiers.
|
||||
*/
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event type.
|
||||
*
|
||||
* @return The event type.
|
||||
*/
|
||||
public EventType getEventType() {
|
||||
return eventType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the key was released.
|
||||
*
|
||||
* @return {@code true} if the event type equals {@code EventType.RELEASED}, otherwise {@code false}.
|
||||
*/
|
||||
public boolean isReleased() {
|
||||
return eventType == EventType.RELEASED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the Shift key is down.
|
||||
*/
|
||||
public boolean isShiftDown() {
|
||||
return (modifiers & SHIFT_MASK) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the Control key is down.
|
||||
*/
|
||||
public boolean isControlDown() {
|
||||
return (modifiers & CTRL_MASK) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the Alt key is down.
|
||||
*/
|
||||
public boolean isAltDown() {
|
||||
return (modifiers & ALT_MASK) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the AltGraph key is down.
|
||||
*/
|
||||
public boolean isAltGraphDown() {
|
||||
return (modifiers & ALT_GRAPH_MASK) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((eventType == null) ? 0 : eventType.hashCode());
|
||||
result = prime * result + keyCode;
|
||||
result = prime * result + modifiers;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
KeyEvent other = (KeyEvent) obj;
|
||||
|
||||
return eventType == other.eventType && keyCode == other.keyCode && modifiers == other.modifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KeyCode: " + keyCode + ", " +
|
||||
"EventType: " + eventType + ", " +
|
||||
"Shift: " + isShiftDown() + ", " +
|
||||
"Control: " + isControlDown() + ", " +
|
||||
"Alt: " + isAltDown() + ", " +
|
||||
"AltGraph: " + isAltGraphDown();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.io;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
public abstract class ResourceLoader {
|
||||
|
||||
/**
|
||||
* Finds a resource of the specified name from the search path.
|
||||
*
|
||||
* @param path The resource path.
|
||||
*
|
||||
* @return A {@link URL} for reading the resource, or {@code null} if the resource could not be found.
|
||||
*/
|
||||
public static URL getResourceURL(String path) {
|
||||
return ClassLoader.getSystemResource(path);
|
||||
}
|
||||
|
||||
/** @see java.lang.ClassLoader#getResourceAsStream(String) */
|
||||
public static InputStream getResourceAsStream(String path) {
|
||||
return ResourceLoader.class.getClassLoader().getResourceAsStream(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the specified {@link URL} has the jar protocol.
|
||||
*
|
||||
* @param url The {@link URL}.
|
||||
* @return {@code true} if the protocol of the specified {@link URL} is the jar protocol, otherwise {@code false}.
|
||||
*/
|
||||
public static boolean isJarResource(URL url) {
|
||||
return url.getProtocol().equals("jar");
|
||||
}
|
||||
|
||||
public static String getJarPath(Class<?> cls) throws URISyntaxException {
|
||||
return cls.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.io.file.visitor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
public class CleanDirVisitor extends SimpleFileVisitor<Path> {
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Files.delete(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.io.file.visitor;
|
||||
|
||||
import static java.util.Objects.nonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.List;
|
||||
|
||||
import moodle.sync.core.util.FileUtils;
|
||||
|
||||
public class CopyDirVisitor extends SimpleFileVisitor<Path> {
|
||||
|
||||
/** The {@link Path} from where to copy. */
|
||||
private final Path fromPath;
|
||||
|
||||
/** The {@link Path} to which to copy. */
|
||||
private final Path toPath;
|
||||
|
||||
/** The copy option. */
|
||||
private final StandardCopyOption copyOption;
|
||||
|
||||
/** The skip list. */
|
||||
private final List<String> skipList;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link CopyDirVisitor} with the specified {@link Path} from where to copy and
|
||||
* the {@link Path} to which to copy.
|
||||
* (Calls {@link #CopyDirVisitor(Path, Path, StandardCopyOption, List)} with
|
||||
* {@code StandardCopyOption.REPLACE_EXISTING} as copy option and {@code null} as skip list.)
|
||||
*
|
||||
* @param fromPath The {@link Path} from where to copy.
|
||||
* @param toPath The {@link Path} to which to copy.
|
||||
*/
|
||||
public CopyDirVisitor(Path fromPath, Path toPath) {
|
||||
this(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link CopyDirVisitor} with the specified {@link Path} from where to copy,
|
||||
* the {@link Path} to which to copy and the skip list.
|
||||
* (Calls {@link #CopyDirVisitor(Path, Path, StandardCopyOption, List)} with
|
||||
* {@code StandardCopyOption.REPLACE_EXISTING} as copy option.)
|
||||
*
|
||||
* @param fromPath The {@link Path} from where to copy.
|
||||
* @param toPath The {@link Path} to which to copy.
|
||||
* @param skipList The skip list.
|
||||
*/
|
||||
public CopyDirVisitor(Path fromPath, Path toPath, List<String> skipList) {
|
||||
this(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING, skipList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link CopyDirVisitor} with the specified {@link Path} from where to copy,
|
||||
* the {@link Path} to which to copy, the copy option and the skip list.
|
||||
*
|
||||
* @param fromPath The {@link Path} from where to copy.
|
||||
* @param toPath The {@link Path} to which to copy.
|
||||
* @param copyOption The copy option.
|
||||
* @param skipList The skip list.
|
||||
*/
|
||||
public CopyDirVisitor(Path fromPath, Path toPath, StandardCopyOption copyOption, List<String> skipList) {
|
||||
this.fromPath = fromPath;
|
||||
this.toPath = toPath;
|
||||
this.copyOption = copyOption;
|
||||
this.skipList = skipList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||
Path targetPath = toPath.resolve(fromPath.relativize(dir));
|
||||
if (!Files.exists(targetPath)) {
|
||||
Files.createDirectory(targetPath);
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
boolean copy = !nonNull(skipList) || !skipList.contains(
|
||||
FileUtils.getExtension(file.toString()));
|
||||
|
||||
if (copy) {
|
||||
Files.copy(file, toPath.resolve(fromPath.relativize(file)), copyOption);
|
||||
}
|
||||
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.io.file.visitor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
public class DeleteDirVisitor extends SimpleFileVisitor<Path> {
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Files.deleteIfExists(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||
Files.deleteIfExists(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.log;
|
||||
|
||||
import org.apache.logging.log4j.core.LoggerContext;
|
||||
import org.apache.logging.log4j.core.config.Configuration;
|
||||
import org.apache.logging.log4j.core.config.ConfigurationFactory;
|
||||
import org.apache.logging.log4j.core.config.ConfigurationSource;
|
||||
import org.apache.logging.log4j.core.config.Order;
|
||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
import org.apache.logging.log4j.core.config.xml.XmlConfiguration;
|
||||
|
||||
@Plugin(name = "Log4jXMLConfigurationFactory", category = "ConfigurationFactory")
|
||||
@Order(10)
|
||||
public class Log4jXMLConfigurationFactory extends ConfigurationFactory {
|
||||
|
||||
/**
|
||||
* Valid file extensions for XML files.
|
||||
*/
|
||||
public static final String[] SUFFIXES = new String[] { ".xml", "*" };
|
||||
|
||||
|
||||
@Override
|
||||
public String[] getSupportedTypes() {
|
||||
return SUFFIXES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration getConfiguration(LoggerContext context, ConfigurationSource source) {
|
||||
return new XmlConfiguration(context, source);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (C) 2021 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.model;
|
||||
|
||||
import static java.util.Objects.nonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Objects;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
/**
|
||||
* Version information container.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class VersionInfo {
|
||||
|
||||
/**
|
||||
* The download {@link URL} for the package.
|
||||
*/
|
||||
public URL downloadUrl;
|
||||
|
||||
/**
|
||||
* The website {@link URL}.
|
||||
*/
|
||||
public URL htmlUrl;
|
||||
|
||||
/**
|
||||
* The version string.
|
||||
*/
|
||||
public String version;
|
||||
|
||||
/**
|
||||
* The publish date.
|
||||
*/
|
||||
public LocalDateTime published;
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the version of the running application. The version of deployed
|
||||
* applications will be retrieved from the {@code Package-Version} entry in
|
||||
* the application's manifest. The default value is {@code dev}.
|
||||
*
|
||||
* @return The application version.
|
||||
*/
|
||||
public static String getAppVersion() {
|
||||
String version;
|
||||
|
||||
try {
|
||||
version = getManifestValue("Package-Version");
|
||||
|
||||
Objects.requireNonNull(version);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Dev mode.
|
||||
version = "dev";
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the application publish date. The date of deployed applications
|
||||
* will be retrieved from the {@code Build-Date} entry in the application's
|
||||
* manifest. The default value is the current date-time from the system
|
||||
* clock.
|
||||
*
|
||||
* @return The application publish date.
|
||||
*/
|
||||
public static LocalDateTime getAppPublishDate() {
|
||||
LocalDateTime date;
|
||||
|
||||
try {
|
||||
String pkgBuildDate = getManifestValue("Build-Date");
|
||||
|
||||
DateTimeFormatter pkgDateFormatter = DateTimeFormatter
|
||||
.ofPattern("yyyy-MM-dd HH:mm");
|
||||
|
||||
date = LocalDateTime.parse(pkgBuildDate, pkgDateFormatter);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Dev mode.
|
||||
date = LocalDateTime.now();
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
private static String getManifestValue(String key) throws IOException {
|
||||
String value = null;
|
||||
Enumeration<URL> resources = VersionInfo.class.getClassLoader()
|
||||
.getResources(JarFile.MANIFEST_NAME);
|
||||
|
||||
while (resources.hasMoreElements()) {
|
||||
try {
|
||||
Manifest manifest = new Manifest(resources.nextElement()
|
||||
.openStream());
|
||||
|
||||
Attributes attr = manifest.getMainAttributes();
|
||||
value = attr.getValue(key);
|
||||
|
||||
if (nonNull(value)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package moodle.sync.core.presenter;
|
||||
|
||||
import moodle.sync.core.app.ApplicationContext;
|
||||
import moodle.sync.core.view.Action;
|
||||
import moodle.sync.core.view.ConfirmationNotificationView;
|
||||
import moodle.sync.core.view.NotificationType;
|
||||
import moodle.sync.core.view.ViewLayer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
||||
/**
|
||||
* Generic notification class used for notification windows with both an accept and decline option.
|
||||
*/
|
||||
public class ConfirmationNotificationPresenter extends Presenter<ConfirmationNotificationView> {
|
||||
|
||||
@Inject
|
||||
public ConfirmationNotificationPresenter(ApplicationContext context, ConfirmationNotificationView view) {
|
||||
super(context, view);
|
||||
}
|
||||
|
||||
public void setNotificationType(NotificationType type) {
|
||||
view.setType(type);
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
view.setTitle(title);
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
view.setMessage(message);
|
||||
}
|
||||
|
||||
public void setConfirmationAction(Action action) {
|
||||
view.setOnConfirm(() -> {
|
||||
action.execute();
|
||||
close();
|
||||
});
|
||||
}
|
||||
|
||||
public void setDiscardAction(Action action) {
|
||||
view.setOnDiscard(() -> {
|
||||
action.execute();
|
||||
close();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewLayer getViewLayer() {
|
||||
return ViewLayer.Notification;
|
||||
}
|
||||
|
||||
public void setConfirmButtonText(String confirmButtonText) {
|
||||
view.setConfirmButtonText(confirmButtonText);
|
||||
}
|
||||
|
||||
public void setDiscardButtonText(String closeButtonText) {
|
||||
view.setDiscardButtonText(closeButtonText);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.presenter;
|
||||
|
||||
import moodle.sync.core.app.ApplicationContext;
|
||||
import moodle.sync.core.view.View;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param <T> The type of the view.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public abstract class MainPresenter<T extends View> extends Presenter<T> {
|
||||
|
||||
public MainPresenter(ApplicationContext context, T view) {
|
||||
super(context, view);
|
||||
}
|
||||
|
||||
abstract public void openFile(final File file);
|
||||
|
||||
abstract public void setArgs(String[] args);
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (C) 2021 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.presenter;
|
||||
|
||||
import java.awt.Desktop;
|
||||
import java.net.URL;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import moodle.sync.core.app.ApplicationContext;
|
||||
import moodle.sync.core.app.dictionary.Dictionary;
|
||||
import moodle.sync.core.model.VersionInfo;
|
||||
import moodle.sync.core.view.NewVersionView;
|
||||
import moodle.sync.core.view.NotificationType;
|
||||
import moodle.sync.core.view.ViewLayer;
|
||||
|
||||
|
||||
public class NewVersionPresenter extends Presenter<NewVersionView> {
|
||||
|
||||
private VersionInfo version;
|
||||
|
||||
|
||||
@Inject
|
||||
NewVersionPresenter(ApplicationContext context, NewVersionView view) {
|
||||
super(context, view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
view.setType(NotificationType.DEFAULT);
|
||||
view.setOnClose(this::close);
|
||||
view.setOnDownload(this::download);
|
||||
view.setOnOpenUrl(this::openUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewLayer getViewLayer() {
|
||||
return ViewLayer.Dialog;
|
||||
}
|
||||
|
||||
public void setVersion(VersionInfo version) {
|
||||
this.version = version;
|
||||
|
||||
String versionStr = version.version;
|
||||
versionStr = versionStr.startsWith("v") ?
|
||||
versionStr.substring(1) :
|
||||
versionStr;
|
||||
|
||||
DateTimeFormatter dateFormatter = DateTimeFormatter
|
||||
.ofLocalizedDate(FormatStyle.LONG)
|
||||
.withLocale(context.getConfiguration().getLocale());
|
||||
|
||||
Dictionary dict = context.getDictionary();
|
||||
String message = MessageFormat.format(dict.get("version.message"),
|
||||
versionStr, version.published.format(dateFormatter),
|
||||
VersionInfo.getAppVersion(),
|
||||
VersionInfo.getAppPublishDate().format(dateFormatter));
|
||||
|
||||
view.setTitle(dict.get("version.new"));
|
||||
view.setMessage(message);
|
||||
}
|
||||
|
||||
private void openUrl() {
|
||||
browse(version.htmlUrl);
|
||||
}
|
||||
|
||||
private void download() {
|
||||
browse(version.downloadUrl);
|
||||
}
|
||||
|
||||
private void browse(URL url) {
|
||||
try {
|
||||
Desktop.getDesktop().browse(url.toURI());
|
||||
}
|
||||
catch (Exception e) {
|
||||
logException(e, "Browse URL failed");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.presenter;
|
||||
|
||||
import moodle.sync.core.app.ApplicationContext;
|
||||
import moodle.sync.core.geometry.Position;
|
||||
import moodle.sync.core.view.NotificationPopupView;
|
||||
import moodle.sync.core.view.NotificationType;
|
||||
import moodle.sync.core.view.ViewLayer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class NotificationPopupPresenter extends Presenter<NotificationPopupView> {
|
||||
|
||||
@Inject
|
||||
NotificationPopupPresenter(ApplicationContext context, NotificationPopupView view) {
|
||||
super(context, view);
|
||||
}
|
||||
|
||||
public void setNotificationType(NotificationType type) {
|
||||
view.setType(type);
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
view.setTitle(title);
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
view.setMessage(message);
|
||||
}
|
||||
|
||||
public void setPosition(Position position) {
|
||||
view.setPosition(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewLayer getViewLayer() {
|
||||
return ViewLayer.NotificationPopup;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.presenter;
|
||||
|
||||
import moodle.sync.core.app.ApplicationContext;
|
||||
import moodle.sync.core.view.NotificationType;
|
||||
import moodle.sync.core.view.NotificationView;
|
||||
import moodle.sync.core.view.ViewLayer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Notification presenter implementation. Notifications will be shown on the
|
||||
* {@code Notification} layer at the top-most view layer.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class NotificationPresenter extends Presenter<NotificationView> {
|
||||
|
||||
@Inject
|
||||
NotificationPresenter(ApplicationContext context, NotificationView view) {
|
||||
super(context, view);
|
||||
}
|
||||
|
||||
public void setNotificationType(NotificationType type) {
|
||||
view.setType(type);
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
view.setTitle(title);
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
view.setMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
view.setOnClose(this::close);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewLayer getViewLayer() {
|
||||
return ViewLayer.Notification;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.presenter;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import moodle.sync.core.app.ApplicationContext;
|
||||
import moodle.sync.core.view.Action;
|
||||
import moodle.sync.core.view.View;
|
||||
import moodle.sync.core.view.ViewLayer;
|
||||
|
||||
import static java.util.Objects.nonNull;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Alex Andres
|
||||
*
|
||||
* @param <T> The type of the view.
|
||||
*/
|
||||
public abstract class Presenter<T extends View> {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(Presenter.class);
|
||||
|
||||
protected final ApplicationContext context;
|
||||
|
||||
protected final T view;
|
||||
|
||||
/** The action that is executed when the user clicks the 'close' button. */
|
||||
protected Action closeAction;
|
||||
|
||||
private boolean closeable;
|
||||
|
||||
|
||||
protected Presenter(ApplicationContext context, T view) {
|
||||
requireNonNull(context);
|
||||
requireNonNull(view);
|
||||
|
||||
this.context = context;
|
||||
this.view = view;
|
||||
this.closeable = true;
|
||||
}
|
||||
|
||||
public T getView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
public ViewLayer getViewLayer() {
|
||||
return ViewLayer.Content;
|
||||
}
|
||||
|
||||
public boolean cache() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (nonNull(closeAction) && isCloseable()) {
|
||||
closeAction.execute();
|
||||
}
|
||||
}
|
||||
|
||||
public void initialize() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
|
||||
public void setOnClose(Action action) {
|
||||
closeAction = Action.concatenate(closeAction, action);
|
||||
}
|
||||
|
||||
protected boolean isCloseable() {
|
||||
return closeable;
|
||||
}
|
||||
|
||||
protected void setCloseable(boolean closeable) {
|
||||
this.closeable = closeable;
|
||||
}
|
||||
|
||||
protected void handleException(Throwable throwable, String throwMessage, String title) {
|
||||
handleException(throwable, throwMessage, title, null);
|
||||
}
|
||||
|
||||
protected final void handleException(Throwable throwable, String throwMessage, String title, String message) {
|
||||
logException(throwable, throwMessage);
|
||||
|
||||
context.showError(title, message);
|
||||
}
|
||||
|
||||
protected final void logMessage(String message, Object... messageParams) {
|
||||
LOG.debug(message, messageParams);
|
||||
}
|
||||
|
||||
protected final void logException(Throwable throwable, String throwMessage) {
|
||||
requireNonNull(throwable);
|
||||
requireNonNull(throwMessage);
|
||||
|
||||
LOG.error(throwMessage, throwable);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.presenter.command;
|
||||
|
||||
public class CloseApplicationCommand {
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.presenter.command;
|
||||
|
||||
import moodle.sync.core.presenter.Presenter;
|
||||
|
||||
public class ClosePresenterCommand {
|
||||
|
||||
/** The presenter class. */
|
||||
private final Class<? extends Presenter<?>> cls;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link ClosePresenterCommand} with the specified presenter class.
|
||||
*
|
||||
* @param cls The presenter class.
|
||||
*/
|
||||
public ClosePresenterCommand(Class<? extends Presenter<?>> cls) {
|
||||
this.cls = cls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the presenter class.
|
||||
*
|
||||
* @return The presenter class.
|
||||
*/
|
||||
public Class<? extends Presenter<?>> getPresenterClass() {
|
||||
return cls;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package moodle.sync.core.presenter.command;
|
||||
|
||||
import moodle.sync.core.presenter.ConfirmationNotificationPresenter;
|
||||
import moodle.sync.core.view.Action;
|
||||
import moodle.sync.core.view.NotificationType;
|
||||
|
||||
|
||||
public class ConfirmationNotificationCommand extends ShowPresenterCommand<ConfirmationNotificationPresenter> {
|
||||
private final NotificationType type;
|
||||
private final String title;
|
||||
private final String message;
|
||||
private final Action confirmAction;
|
||||
private final String confirmButtonText;
|
||||
private final String closeButtonText;
|
||||
private final Action discardAction;
|
||||
|
||||
public ConfirmationNotificationCommand(NotificationType type, String title, String message, Action confirmAction, Action discardAction, String confirmButtonText, String closeButtonText) {
|
||||
super(ConfirmationNotificationPresenter.class);
|
||||
|
||||
this.type = type;
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
this.confirmAction = confirmAction;
|
||||
this.discardAction = discardAction;
|
||||
this.confirmButtonText = confirmButtonText;
|
||||
this.closeButtonText = closeButtonText;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void execute(ConfirmationNotificationPresenter presenter) {
|
||||
presenter.initialize();
|
||||
presenter.setNotificationType(type);
|
||||
presenter.setTitle(title);
|
||||
presenter.setMessage(message);
|
||||
presenter.setConfirmationAction(confirmAction);
|
||||
presenter.setDiscardAction(discardAction);
|
||||
presenter.setConfirmButtonText(confirmButtonText);
|
||||
presenter.setDiscardButtonText(closeButtonText);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2021 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.presenter.command;
|
||||
|
||||
import moodle.sync.core.model.VersionInfo;
|
||||
import moodle.sync.core.presenter.NewVersionPresenter;
|
||||
|
||||
public class NewVersionCommand extends ShowPresenterCommand<NewVersionPresenter> {
|
||||
|
||||
/** The new version. */
|
||||
private final VersionInfo version;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link NewVersionCommand} with the specified parameters.
|
||||
*
|
||||
* @param cls The presenter class.
|
||||
* @param version The new version.
|
||||
*/
|
||||
public NewVersionCommand(Class<NewVersionPresenter> cls, VersionInfo version) {
|
||||
super(cls);
|
||||
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(NewVersionPresenter presenter) {
|
||||
presenter.setVersion(version);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.presenter.command;
|
||||
|
||||
import moodle.sync.core.presenter.NotificationPresenter;
|
||||
import moodle.sync.core.view.NotificationType;
|
||||
|
||||
/**
|
||||
* Notification view command implementation.
|
||||
* Notifications will be shown on the {@code Notification} layer at the top-most view layer.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class NotificationCommand extends ShowPresenterCommand<NotificationPresenter> {
|
||||
|
||||
/** The notification type, error, warning etc. */
|
||||
private final NotificationType type;
|
||||
|
||||
/** The title of the notification. */
|
||||
private final String title;
|
||||
|
||||
/** The message of the notification. */
|
||||
private final String message;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link NotificationCommand} with the specified notification type and title. The message is empty.
|
||||
*
|
||||
* @param type The type of the notification.
|
||||
* @param title The title of the notification.
|
||||
*/
|
||||
public NotificationCommand(NotificationType type, String title) {
|
||||
this(type, title, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link NotificationCommand} with the specified notification type, title and message.
|
||||
*
|
||||
* @param type The type of the notification.
|
||||
* @param title The title of the notification.
|
||||
* @param message The message of the notification.
|
||||
*/
|
||||
public NotificationCommand(NotificationType type, String title, String message) {
|
||||
super(NotificationPresenter.class);
|
||||
|
||||
this.type = type;
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(NotificationPresenter presenter) {
|
||||
presenter.setNotificationType(type);
|
||||
presenter.setTitle(title);
|
||||
presenter.setMessage(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.presenter.command;
|
||||
|
||||
import moodle.sync.core.geometry.Position;
|
||||
import moodle.sync.core.presenter.NotificationPopupPresenter;
|
||||
import moodle.sync.core.view.NotificationType;
|
||||
|
||||
/**
|
||||
* NotificationPopup view command implementation. NotificationPopups will be shown on
|
||||
* the {@code NotificationPopup} layer at the top-most view layer with configurable position in the main window.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class NotificationPopupCommand extends ShowPresenterCommand<NotificationPopupPresenter> {
|
||||
|
||||
/** The position of the popup in the main window. */
|
||||
private final Position position;
|
||||
|
||||
/** The notification type, error, warning etc. */
|
||||
private final NotificationType type;
|
||||
|
||||
/** The title of the notification. */
|
||||
private final String title;
|
||||
|
||||
/** The message of the notification. */
|
||||
private final String message;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link NotificationPopupCommand} with the specified notification type and title.
|
||||
* The message is empty.
|
||||
*
|
||||
* @param position The position of the popup in the main window.
|
||||
* @param title The title of the notification.
|
||||
* @param message The message of the notification.
|
||||
*/
|
||||
public NotificationPopupCommand(Position position, String title, String message) {
|
||||
this(position, NotificationType.DEFAULT, title, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link NotificationPopupCommand} with the specified notification
|
||||
* type, title and message at the given position.
|
||||
*
|
||||
* @param position The position of the popup in the main window.
|
||||
* @param type The type of the notification.
|
||||
* @param title The title of the notification.
|
||||
* @param message The message of the notification.
|
||||
*/
|
||||
public NotificationPopupCommand(Position position, NotificationType type, String title, String message) {
|
||||
super(NotificationPopupPresenter.class);
|
||||
|
||||
this.position = position;
|
||||
this.type = type;
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(NotificationPopupPresenter presenter) {
|
||||
presenter.setPosition(position);
|
||||
presenter.setNotificationType(type);
|
||||
presenter.setTitle(title);
|
||||
presenter.setMessage(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.presenter.command;
|
||||
|
||||
import moodle.sync.core.presenter.Presenter;
|
||||
|
||||
public class ShowPresenterCommand<T extends Presenter<?>> {
|
||||
|
||||
/** The presenter class. */
|
||||
private final Class<T> cls;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link ShowPresenterCommand} with the specified presenter class.
|
||||
*
|
||||
* @param cls The presenter class.
|
||||
*/
|
||||
public ShowPresenterCommand(Class<T> cls) {
|
||||
this.cls = cls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the presenter class.
|
||||
*
|
||||
* @return The presenter class.
|
||||
*/
|
||||
public Class<T> getPresenterClass() {
|
||||
return cls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command on the specified presenter.
|
||||
* May be empty if no extra commands are required to display the corresponding view.
|
||||
*
|
||||
* @param presenter The presenter.
|
||||
*/
|
||||
public void execute(T presenter) {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* A ResourceBundle whose content is aggregated from multiple bundles.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public class AggregateBundle extends ResourceBundle {
|
||||
|
||||
private final Map<String, Object> contents;
|
||||
|
||||
/** The locale for this bundle. */
|
||||
private final Locale locale;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new AggregateBundle.
|
||||
*
|
||||
* @param locale The locale for this bundle.
|
||||
* @param bundleNames A list of bundles which shall be merged into this bundle.
|
||||
*
|
||||
* @throws IOException if one or more bundles could not be loaded.
|
||||
*/
|
||||
public AggregateBundle(Locale locale, String... bundleNames) throws IOException {
|
||||
this.contents = new HashMap<>();
|
||||
this.locale = locale;
|
||||
|
||||
load(bundleNames);
|
||||
}
|
||||
|
||||
public void load(String... bundleNames) throws IOException {
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
for (String name : bundleNames) {
|
||||
if (name.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String resourcePath = name.replaceAll("\\.", "/");
|
||||
File bundlePath = new File(resourcePath);
|
||||
String parentPath = bundlePath.getParent().replace("\\", "/");
|
||||
String baseName = bundlePath.getName();
|
||||
|
||||
scanForBundles(cl, parentPath, baseName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(String key) {
|
||||
return contents.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getKeys() {
|
||||
return new IteratorEnumeration<>(contents.keySet().iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleGetObject(String key) {
|
||||
return contents.get(key);
|
||||
}
|
||||
|
||||
private void scanForBundles(ClassLoader cl, String path, String baseName) throws IOException {
|
||||
Control control = Control.getControl(Control.FORMAT_DEFAULT);
|
||||
List<Locale> candidateLocales = control.getCandidateLocales(baseName, locale);
|
||||
Enumeration<URL> names = cl.getResources(path);
|
||||
|
||||
candidateLocales.removeIf(locale -> locale == Locale.ROOT);
|
||||
|
||||
while (names.hasMoreElements()) {
|
||||
final URL url = names.nextElement();
|
||||
String protocol = url.getProtocol();
|
||||
|
||||
if (protocol.equals("file")) {
|
||||
File parent = new File(url.getFile());
|
||||
|
||||
if (parent.isDirectory()) {
|
||||
String[] list = parent.list();
|
||||
|
||||
if (isNull(list)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (String name : list) {
|
||||
File file = new File(name);
|
||||
|
||||
if (!file.isDirectory() && name.startsWith(baseName) && name.endsWith(".properties")) {
|
||||
// Extract locale from file name.
|
||||
Locale tagLocale = FileUtils.extractLocale(name, baseName);
|
||||
|
||||
if (!candidateLocales.contains(tagLocale)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
File resource = new File(url.getFile() + "/" + name);
|
||||
|
||||
loadBundle(resource.toURI().toURL().openStream());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (protocol.equals("jar")) {
|
||||
String searchPath = path + "/" + baseName;
|
||||
String urlPath = url.getPath();
|
||||
urlPath = urlPath.substring(0, urlPath.indexOf("!"));
|
||||
|
||||
File jarFile;
|
||||
|
||||
try {
|
||||
jarFile = Paths.get(new URL(urlPath).toURI()).toFile();
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
JarFile jar = new JarFile(jarFile);
|
||||
Enumeration<JarEntry> entries = jar.entries();
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String name = entry.getName();
|
||||
|
||||
if (!entry.isDirectory() && name.startsWith(searchPath) && name.endsWith(".properties")) {
|
||||
// Extract locale from file name.
|
||||
Locale tagLocale = FileUtils.extractLocale(name, baseName);
|
||||
|
||||
if (!candidateLocales.contains(tagLocale)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
loadBundle(jar.getInputStream(entry));
|
||||
}
|
||||
}
|
||||
|
||||
jar.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadBundle(InputStream stream) throws IOException {
|
||||
if (isNull(stream)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (stream) {
|
||||
Properties props = new Properties();
|
||||
props.load(stream);
|
||||
|
||||
Enumeration<Object> keys = props.keys();
|
||||
|
||||
while (keys.hasMoreElements()) {
|
||||
String key = (String) keys.nextElement();
|
||||
|
||||
if (!contents.containsKey(key)) {
|
||||
contents.put(key, props.getProperty(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An Enumeration implementation that wraps an Iterator.
|
||||
*
|
||||
* @param <T> The enumerated type.
|
||||
*/
|
||||
private static class IteratorEnumeration<T> implements Enumeration<T> {
|
||||
|
||||
private final Iterator<T> source;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new IterationEnumeration.
|
||||
*
|
||||
* @param source The source iterator.
|
||||
*/
|
||||
IteratorEnumeration(Iterator<T> source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
return source.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T nextElement() {
|
||||
return source.next();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
import moodle.sync.core.io.file.visitor.CleanDirVisitor;
|
||||
import moodle.sync.core.io.file.visitor.CopyDirVisitor;
|
||||
import moodle.sync.core.io.file.visitor.DeleteDirVisitor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitOption;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public final class DirUtils {
|
||||
|
||||
private DirUtils() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks file tree starting at the given path and deletes all files
|
||||
* but leaves the directory structure intact.
|
||||
*
|
||||
* @param path the base path to start from.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void clean(Path path) throws IOException {
|
||||
validate(path);
|
||||
Files.walkFileTree(path, new CleanDirVisitor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely removes given file tree starting at and including the given path.
|
||||
*
|
||||
* @param path
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void delete(Path path) throws IOException {
|
||||
validate(path);
|
||||
Files.walkFileTree(path, new DeleteDirVisitor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a directory tree
|
||||
*
|
||||
* @param from
|
||||
* @param to
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void copy(Path from, Path to) throws IOException {
|
||||
validate(from);
|
||||
Files.walkFileTree(from, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new CopyDirVisitor(from, to));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a directory tree by excluding files having a extension that is
|
||||
* noted in the provided skip-list.
|
||||
*
|
||||
* @param from The source path.
|
||||
* @param to The target path.
|
||||
* @param skipList The list containing extensions that should be excluded.
|
||||
*
|
||||
* @throws IOException If the path could not be copied.
|
||||
*/
|
||||
public static void copy(Path from, Path to, List<String> skipList) throws IOException {
|
||||
validate(from);
|
||||
Files.walkFileTree(from, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new CopyDirVisitor(from, to, skipList));
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves one directory tree to another. Not a true move operation in that the
|
||||
* directory tree is copied, then the original directory tree is deleted.
|
||||
*
|
||||
* @param from
|
||||
* @param to
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void move(Path from, Path to) throws IOException {
|
||||
validate(from);
|
||||
Files.walkFileTree(from, new CopyDirVisitor(from, to));
|
||||
Files.walkFileTree(from, new DeleteDirVisitor());
|
||||
}
|
||||
|
||||
public static void createIfNotExists(Path path) throws IOException {
|
||||
if (!Files.exists(path)) {
|
||||
Files.createDirectories(path);
|
||||
}
|
||||
}
|
||||
|
||||
private static void validate(Path... paths) {
|
||||
for (Path path : paths) {
|
||||
if (!Files.isDirectory(path)) {
|
||||
throw new IllegalArgumentException(String.format("%s is not a directory", path.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
import moodle.sync.core.app.configuration.Configuration;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
import static java.util.Objects.nonNull;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
/**
|
||||
* The default path resolves to 'user.home'.
|
||||
*/
|
||||
public static final String DEFAULT_PATH = System.getProperty("user.home");
|
||||
|
||||
|
||||
public static Path getContextPath(Configuration config, String pathContext) {
|
||||
return getContextPath(config, pathContext, DEFAULT_PATH);
|
||||
}
|
||||
|
||||
public static Path getContextPath(Configuration config, String pathContext, String defaultPath) {
|
||||
if (isNull(defaultPath) || defaultPath.isEmpty()) {
|
||||
defaultPath = DEFAULT_PATH;
|
||||
}
|
||||
|
||||
Map<String, String> contextPaths = config.getContextPaths();
|
||||
String pathStr = contextPaths.getOrDefault(pathContext, defaultPath);
|
||||
Path dirPath = Paths.get(pathStr);
|
||||
|
||||
if (Files.notExists(dirPath) || !Files.isDirectory(dirPath)) {
|
||||
dirPath = Paths.get(defaultPath);
|
||||
}
|
||||
|
||||
return dirPath;
|
||||
}
|
||||
|
||||
public static File ensureExtension(File file, String extension) {
|
||||
String path = file.getAbsolutePath();
|
||||
|
||||
if (!path.endsWith(extension)) {
|
||||
return new File(path + extension);
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
public static File stripExtension(File file) {
|
||||
String path = file.getPath();
|
||||
int extensionIndex = path.lastIndexOf(".");
|
||||
if (extensionIndex != -1) {
|
||||
path = path.substring(0, extensionIndex);
|
||||
}
|
||||
|
||||
return new File(path);
|
||||
}
|
||||
|
||||
public static String stripExtension(String path) {
|
||||
int extensionIndex = path.lastIndexOf(".");
|
||||
if (extensionIndex != -1) {
|
||||
path = path.substring(0, extensionIndex);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static String getExtension(String path) {
|
||||
int extensionIndex = path.lastIndexOf(".");
|
||||
int pathSeparatorIndex = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
||||
int index = (pathSeparatorIndex > extensionIndex ? -1 : extensionIndex);
|
||||
|
||||
if (index == -1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return path.substring(index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the decoded path string with %20 in original path string replaced
|
||||
* by white space character.
|
||||
*/
|
||||
public static String decodePath(String path) {
|
||||
try {
|
||||
return new URI(null, path, null).getPath();
|
||||
}
|
||||
catch (Exception e) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public static Locale extractLocale(String path, String baseName) {
|
||||
String tag = stripExtension(path);
|
||||
tag = tag.substring(tag.lastIndexOf(baseName) + baseName.length());
|
||||
tag = tag.replace("_", "-");
|
||||
|
||||
if (tag.startsWith("-")) {
|
||||
tag = tag.substring(1);
|
||||
}
|
||||
|
||||
return Locale.forLanguageTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the directory named by this abstract pathname, including any
|
||||
* necessary but nonexistent parent directories. Note that if this operation
|
||||
* fails it may have succeeded in creating necessary parent directories.
|
||||
*
|
||||
* @return {@code true} if and only if the directory was created, along with
|
||||
* all necessary parent directories, otherwise {@code false}.
|
||||
*/
|
||||
public static boolean create(String path) {
|
||||
File file = new File(path);
|
||||
boolean created = false;
|
||||
|
||||
if (!file.exists()) {
|
||||
created = file.mkdirs();
|
||||
}
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
public static void copyJarResource(String jarPath, String source, String destDir, List<String> skipList) throws IOException {
|
||||
final JarFile jarFile = new JarFile(jarPath);
|
||||
boolean foundExactMatch = false;
|
||||
|
||||
for (final Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements();) {
|
||||
JarEntry entry = e.nextElement();
|
||||
String entryName = entry.getName();
|
||||
File file = new File(entryName);
|
||||
|
||||
if (!file.toPath().normalize().startsWith(source)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entryName.equals(source)) {
|
||||
entryName = getFileName(entryName);
|
||||
|
||||
foundExactMatch = true;
|
||||
}
|
||||
else {
|
||||
entryName = entryName.substring(source.length());
|
||||
if (entryName.endsWith(File.separator)) {
|
||||
entryName = entryName.substring(0, entryName.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
File dest = new File(destDir, entryName);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
dest.mkdir();
|
||||
}
|
||||
else {
|
||||
if (nonNull(skipList) && skipList.contains(FileUtils.getExtension(entryName))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Copy file.
|
||||
InputStream inputStream = jarFile.getInputStream(entry);
|
||||
Files.copy(inputStream, dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
inputStream.close();
|
||||
|
||||
if (foundExactMatch) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jarFile.close();
|
||||
}
|
||||
|
||||
public static String getFileName(String filePath) {
|
||||
int index = filePath.lastIndexOf("\\");
|
||||
|
||||
if (index == -1) {
|
||||
index = filePath.lastIndexOf("/");
|
||||
}
|
||||
if (index != -1) {
|
||||
filePath = filePath.substring(index + 1);
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public static String[] getResourceListing(String path, Predicate<String> predicate) throws Exception {
|
||||
URL dirURL = FileUtils.class.getResource(path);
|
||||
|
||||
if (dirURL == null) {
|
||||
dirURL = ClassLoader.getSystemResource(path);
|
||||
}
|
||||
if (dirURL == null) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
if (dirURL.getProtocol().equals("file")) {
|
||||
List<String> result = new ArrayList<>();
|
||||
Path srcPath = Paths.get(dirURL.toURI());
|
||||
String srcPathName = srcPath.toString();
|
||||
|
||||
Files.walkFileTree(srcPath, new SimpleFileVisitor<>() {
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
|
||||
String filePath = file.toString();
|
||||
|
||||
if (!attrs.isDirectory() && predicate.test(filePath)) {
|
||||
String relativePath = filePath.substring(srcPathName.length());
|
||||
relativePath = relativePath.replace("\\", "/");
|
||||
|
||||
if (relativePath.startsWith("/")) {
|
||||
relativePath = relativePath.substring(1);
|
||||
}
|
||||
|
||||
result.add(path + "/" + relativePath);
|
||||
}
|
||||
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
|
||||
return result.toArray(new String[0]);
|
||||
}
|
||||
else if (dirURL.getProtocol().equals("jar")) {
|
||||
String searchPath = path;
|
||||
|
||||
if (searchPath.startsWith("/")) {
|
||||
searchPath = searchPath.substring(1);
|
||||
}
|
||||
|
||||
String urlPath = dirURL.getPath();
|
||||
urlPath = urlPath.substring(0, urlPath.indexOf("!"));
|
||||
|
||||
File jarFile = Paths.get(new URL(urlPath).toURI()).toFile();
|
||||
|
||||
JarFile jar = new JarFile(jarFile);
|
||||
Enumeration<JarEntry> entries = jar.entries();
|
||||
Set<String> result = new HashSet<>();
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
String name = entries.nextElement().getName();
|
||||
File file = new File(name);
|
||||
|
||||
if (file.toPath().normalize().startsWith(searchPath) && predicate.test(name)) {
|
||||
String relativePath = name.substring(searchPath.length() + 1);
|
||||
|
||||
if (!relativePath.isEmpty()) {
|
||||
result.add(path + "/" + relativePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jar.close();
|
||||
|
||||
return result.toArray(new String[0]);
|
||||
}
|
||||
|
||||
throw new IOException("Cannot list files for path: " + path);
|
||||
}
|
||||
|
||||
public static byte[] getByteArray(File file) throws IOException {
|
||||
Path path = Paths.get(file.toURI());
|
||||
return Files.readAllBytes(path);
|
||||
}
|
||||
|
||||
public static boolean deleteDir(File dir) {
|
||||
if (dir.isDirectory()) {
|
||||
String[] children = dir.list();
|
||||
for (String child : children) {
|
||||
boolean success = deleteDir(new File(dir, child));
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dir.delete();
|
||||
}
|
||||
|
||||
public static String shortenPath(String path, int limit) {
|
||||
if (path.length() <= limit) {
|
||||
return path;
|
||||
}
|
||||
|
||||
String SHORTENER_ELLIPSE = " ... ";
|
||||
|
||||
char[] shortPathArray = new char[limit];
|
||||
char[] pathArray = path.toCharArray();
|
||||
char[] ellipseArray = SHORTENER_ELLIPSE.toCharArray();
|
||||
|
||||
int pathindex = pathArray.length - 1;
|
||||
int shortpathindex = limit - 1;
|
||||
|
||||
// fill the array from the end
|
||||
int i = 0;
|
||||
for (; i < limit; i++) {
|
||||
if (pathArray[pathindex - i] != '/' && pathArray[pathindex - i] != '\\') {
|
||||
shortPathArray[shortpathindex - i] = pathArray[pathindex - i];
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// check how much space is left
|
||||
int free = limit - i;
|
||||
|
||||
if (free < SHORTENER_ELLIPSE.length()) {
|
||||
// fill the beginning with ellipse
|
||||
System.arraycopy(ellipseArray, 0, shortPathArray, 0, ellipseArray.length);
|
||||
}
|
||||
else {
|
||||
// fill the beginning with path and leave room for the ellipse
|
||||
int j = 0;
|
||||
for (; j + ellipseArray.length < free; j++) {
|
||||
shortPathArray[j] = pathArray[j];
|
||||
}
|
||||
// ... add the ellipse
|
||||
for (int k = 0; j + k < free; k++) {
|
||||
shortPathArray[j + k] = ellipseArray[k];
|
||||
}
|
||||
}
|
||||
return new String(shortPathArray);
|
||||
}
|
||||
|
||||
public static void zipFile(File inZipFile, File outZipFile) throws IOException {
|
||||
try (FileOutputStream fos = new FileOutputStream(outZipFile);
|
||||
ZipOutputStream zipOut = new ZipOutputStream(fos)) {
|
||||
zipFile(inZipFile, inZipFile.getName(), zipOut);
|
||||
}
|
||||
}
|
||||
|
||||
private static void zipFile(File inZipFile, String fileName, ZipOutputStream zipOut) throws IOException {
|
||||
if (inZipFile.isHidden()) {
|
||||
return;
|
||||
}
|
||||
if (inZipFile.isDirectory()) {
|
||||
if (fileName.endsWith("/")) {
|
||||
zipOut.putNextEntry(new ZipEntry(fileName));
|
||||
zipOut.closeEntry();
|
||||
}
|
||||
else {
|
||||
zipOut.putNextEntry(new ZipEntry(fileName + "/"));
|
||||
zipOut.closeEntry();
|
||||
}
|
||||
|
||||
File[] children = inZipFile.listFiles();
|
||||
for (File childFile : children) {
|
||||
zipFile(childFile, fileName + "/" + childFile.getName(), zipOut);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FileInputStream fis = new FileInputStream(inZipFile);
|
||||
ZipEntry zipEntry = new ZipEntry(fileName);
|
||||
zipOut.putNextEntry(zipEntry);
|
||||
|
||||
byte[] bytes = new byte[1024];
|
||||
int length;
|
||||
while ((length = fis.read(bytes)) >= 0) {
|
||||
zipOut.write(bytes, 0, length);
|
||||
}
|
||||
fis.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
/**
|
||||
* The listener that is called when changes to the {@link moodle.sync.core.util.ObservableList} occur.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public interface ListChangeListener<T extends ObservableList<?>> {
|
||||
|
||||
/**
|
||||
* Called whenever one or more elements in the list have changed.
|
||||
*
|
||||
* @param list The changed list.
|
||||
* @param startIndex The starting index of the first changed element.
|
||||
* @param itemCount The number of changed elements.
|
||||
*/
|
||||
default void listItemsChanged(T list, int startIndex, int itemCount) {
|
||||
listChanged(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever elements have been inserted into the list.
|
||||
*
|
||||
* @param list The changed list.
|
||||
* @param startIndex The position of the first inserted element.
|
||||
* @param itemCount The number of inserted elements.
|
||||
*/
|
||||
default void listItemsInserted(T list, int startIndex, int itemCount) {
|
||||
listChanged(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever elements in the list have been moved.
|
||||
*
|
||||
* @param list The changed list.
|
||||
* @param startIndex The starting index from which the elements were moved.
|
||||
* @param destIndex The new position of the elements.
|
||||
* @param itemCount The number of moved elements.
|
||||
*/
|
||||
default void listItemsMoved(T list, int startIndex, int destIndex, int itemCount) {
|
||||
listChanged(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever elements in the list have been deleted.
|
||||
*
|
||||
* @param list The changed list.
|
||||
* @param startIndex The starting index of the first deleted element.
|
||||
* @param itemCount The number of removed elements.
|
||||
*/
|
||||
default void listItemsRemoved(T list, int startIndex, int itemCount) {
|
||||
listChanged(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever items of the list have changed. This method is by default
|
||||
* a no-op.
|
||||
*
|
||||
* @param list The changed list.
|
||||
*/
|
||||
default void listChanged(T list) {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
/**
|
||||
* The listener that is called when changes to the {@link ObservableMap} occur.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*
|
||||
* @param <K> The key type.
|
||||
* @param <V> The value type.
|
||||
*/
|
||||
public interface MapChangeListener<K, V> {
|
||||
|
||||
/**
|
||||
* Called whenever a new key-value pair is added.
|
||||
*
|
||||
* @param map The changed map.
|
||||
* @param key The key.
|
||||
* @param value The value.
|
||||
*/
|
||||
default void mapEntryAdded(ObservableMap<K, V> map, K key, V value) {
|
||||
mapChanged(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever a key-value pair has changed.
|
||||
*
|
||||
* @param map The changed map.
|
||||
* @param key The key.
|
||||
* @param value The value.
|
||||
*/
|
||||
default void mapEntryChanged(ObservableMap<K, V> map, K key, V value) {
|
||||
mapChanged(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever a key-value pair has been removed.
|
||||
*
|
||||
* @param map The changed map.
|
||||
* @param key The key.
|
||||
* @param value The value.
|
||||
*/
|
||||
default void mapEntryRemoved(ObservableMap<K, V> map, K key, V value) {
|
||||
mapChanged(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever items of the map have changed. This method is by default
|
||||
* a no-op.
|
||||
*
|
||||
* @param map The changed map.
|
||||
*/
|
||||
default void mapChanged(ObservableMap<K, V> map) {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* An {@link ObservableList} implementation backed by the {@link ArrayList}.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*
|
||||
* @param <T> The type of elements in the list.
|
||||
*/
|
||||
public class ObservableArrayList<T> extends ArrayList<T> implements ObservableList<T> {
|
||||
|
||||
private static final long serialVersionUID = -2986628478190499263L;
|
||||
|
||||
private final transient List<ListChangeListener<ObservableList<T>>> listeners = new ArrayList<>();
|
||||
|
||||
|
||||
@Override
|
||||
public boolean add(T element) {
|
||||
super.add(element);
|
||||
|
||||
notifyInserted(size() - 1, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, T element) {
|
||||
super.add(index, element);
|
||||
|
||||
notifyInserted(index, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> collection) {
|
||||
int oldSize = size();
|
||||
boolean added = super.addAll(collection);
|
||||
|
||||
if (added) {
|
||||
notifyInserted(oldSize, size() - oldSize);
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends T> collection) {
|
||||
boolean added = super.addAll(index, collection);
|
||||
|
||||
if (added) {
|
||||
notifyInserted(index, collection.size());
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
int oldSize = size();
|
||||
super.clear();
|
||||
|
||||
if (oldSize != 0) {
|
||||
notifyRemoved(0, oldSize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T remove(int index) {
|
||||
T element = super.remove(index);
|
||||
|
||||
notifyRemoved(index, 1);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object object) {
|
||||
int index = indexOf(object);
|
||||
|
||||
if (index >= 0) {
|
||||
remove(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeIf(Predicate<? super T> filter) {
|
||||
boolean removed = super.removeIf(filter);
|
||||
|
||||
if (removed) {
|
||||
notifyRemoved(0, 1);
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
int oldSize = size();
|
||||
boolean changed = super.retainAll(c);
|
||||
|
||||
if (changed) {
|
||||
notifyChanged(0, oldSize);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T set(int index, T object) {
|
||||
T element = super.set(index, object);
|
||||
|
||||
notifyChanged(index, 1);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(ListChangeListener<ObservableList<T>> listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(ListChangeListener<ObservableList<T>> listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeRange(int fromIndex, int toIndex) {
|
||||
super.removeRange(fromIndex, toIndex);
|
||||
|
||||
notifyRemoved(fromIndex, toIndex - fromIndex);
|
||||
}
|
||||
|
||||
private void notifyInserted(int start, int count) {
|
||||
for (ListChangeListener<ObservableList<T>> listener : listeners) {
|
||||
listener.listItemsInserted(this, start, count);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRemoved(int start, int count) {
|
||||
for (ListChangeListener<ObservableList<T>> listener : listeners) {
|
||||
listener.listItemsRemoved(this, start, count);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyChanged(int start, int count) {
|
||||
for (ListChangeListener<ObservableList<T>> listener : listeners) {
|
||||
listener.listItemsChanged(this, start, count);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
import static java.util.Objects.nonNull;
|
||||
|
||||
/**
|
||||
* An {@link ObservableMap} implementation backed by the {@link HashMap}.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*
|
||||
* @param <K> The key type.
|
||||
* @param <V> The value type.
|
||||
*/
|
||||
public class ObservableHashMap<K, V> extends HashMap<K, V> implements ObservableMap<K, V> {
|
||||
|
||||
private static final long serialVersionUID = 1390495316242703829L;
|
||||
|
||||
private final transient List<MapChangeListener<K, V>> listeners = new ArrayList<>();
|
||||
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
int oldSize = size();
|
||||
|
||||
super.clear();
|
||||
|
||||
if (oldSize != 0) {
|
||||
notifyMapChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
V put = super.put(key, value);
|
||||
|
||||
if (isNull(put)) {
|
||||
notifyAdded(key, value);
|
||||
}
|
||||
else if (nonNull(value) && !value.equals(put)) {
|
||||
notifyChanged(key, value);
|
||||
}
|
||||
|
||||
return put;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> map) {
|
||||
super.putAll(map);
|
||||
|
||||
notifyMapChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public V remove(Object key) {
|
||||
V removed = super.remove(key);
|
||||
|
||||
if (nonNull(removed)) {
|
||||
notifyRemoved((K) key, removed);
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(MapChangeListener<K, V> listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(MapChangeListener<K, V> listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
private void notifyAdded(K key, V value) {
|
||||
for (MapChangeListener<K, V> listener : listeners) {
|
||||
listener.mapEntryAdded(this, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyChanged(K key, V value) {
|
||||
for (MapChangeListener<K, V> listener : listeners) {
|
||||
listener.mapEntryChanged(this, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRemoved(K key, V value) {
|
||||
for (MapChangeListener<K, V> listener : listeners) {
|
||||
listener.mapEntryAdded(this, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyMapChanged() {
|
||||
for (MapChangeListener<K, V> listener : listeners) {
|
||||
listener.mapChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An {@link ObservableSet} implementation backed by the {@link HashSet}.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*
|
||||
* @param <T> The type of elements in the set.
|
||||
*/
|
||||
public class ObservableHashSet<T> extends HashSet<T> implements ObservableSet<T> {
|
||||
|
||||
private static final long serialVersionUID = 1390495316242703829L;
|
||||
|
||||
private final transient List<SetChangeListener<ObservableSet<T>>> listeners = new ArrayList<>();
|
||||
|
||||
|
||||
@Override
|
||||
public boolean add(T element) {
|
||||
boolean added = super.add(element);
|
||||
|
||||
if (added) {
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> collection) {
|
||||
boolean added = super.addAll(collection);
|
||||
|
||||
if (added) {
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
int oldSize = size();
|
||||
|
||||
super.clear();
|
||||
|
||||
if (oldSize != 0) {
|
||||
notifyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object object) {
|
||||
boolean removed = super.remove(object);
|
||||
|
||||
if (removed) {
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
boolean removed = super.removeAll(c);
|
||||
|
||||
if (removed) {
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
boolean changed = super.retainAll(c);
|
||||
|
||||
if (changed) {
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(SetChangeListener<ObservableSet<T>> listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(SetChangeListener<ObservableSet<T>> listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
private void notifyChanged() {
|
||||
for (SetChangeListener<ObservableSet<T>> listener : listeners) {
|
||||
listener.setChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link List} that notifies when changes occur.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public interface ObservableList<T> extends List<T> {
|
||||
|
||||
/**
|
||||
* Adds a listener to be notified when changes to the list occur.
|
||||
*
|
||||
* @param listener The listener to be notified on list changes.
|
||||
*/
|
||||
void addListener(ListChangeListener<ObservableList<T>> listener);
|
||||
|
||||
/**
|
||||
* Removes a previously added listener.
|
||||
*
|
||||
* @param listener The listener to remove.
|
||||
*/
|
||||
void removeListener(ListChangeListener<ObservableList<T>> listener);
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A {@link Map} that notifies when changes occur.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*
|
||||
* @param <K> The key type.
|
||||
* @param <V> The value type.
|
||||
*/
|
||||
public interface ObservableMap<K, V> extends Map<K, V> {
|
||||
|
||||
/**
|
||||
* Adds a listener to be notified when changes to the map occur.
|
||||
*
|
||||
* @param listener The listener to be notified on map changes.
|
||||
*/
|
||||
void addListener(MapChangeListener<K, V> listener);
|
||||
|
||||
/**
|
||||
* Removes a previously added listener.
|
||||
*
|
||||
* @param listener The listener to remove.
|
||||
*/
|
||||
void removeListener(MapChangeListener<K, V> listener);
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A {@link Set} that notifies when changes occur.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public interface ObservableSet<T> extends Set<T> {
|
||||
|
||||
/**
|
||||
* Adds a listener to be notified when changes to the list occur.
|
||||
*
|
||||
* @param listener The listener to be notified on list changes.
|
||||
*/
|
||||
void addListener(SetChangeListener<ObservableSet<T>> listener);
|
||||
|
||||
/**
|
||||
* Removes a previously added listener.
|
||||
*
|
||||
* @param listener The listener to remove.
|
||||
*/
|
||||
void removeListener(SetChangeListener<ObservableSet<T>> listener);
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
public class OsInfo {
|
||||
|
||||
private static final String platformName;
|
||||
|
||||
private static String osName;
|
||||
|
||||
private static int bitness;
|
||||
|
||||
|
||||
static {
|
||||
osName = System.getProperty("os.name").toLowerCase();
|
||||
String osArch = System.getProperty("os.arch").toLowerCase();
|
||||
String jvmName = System.getProperty("java.vm.name").toLowerCase();
|
||||
|
||||
if (jvmName.startsWith("dalvik") && osName.startsWith("linux")) {
|
||||
osName = "android";
|
||||
}
|
||||
else if (osName.startsWith("mac os x")) {
|
||||
osName = "macosx";
|
||||
}
|
||||
else {
|
||||
osName = osName.split(" ")[0];
|
||||
}
|
||||
|
||||
if (osArch.endsWith("86")) {
|
||||
osArch = "x86";
|
||||
bitness = 32;
|
||||
}
|
||||
else if (osArch.equals("amd64") || osArch.equals("x86-64") || osArch.equals("x86_64")) {
|
||||
osArch = "x86_64";
|
||||
bitness = 64;
|
||||
}
|
||||
else if (osArch.startsWith("arm")) {
|
||||
osArch = "arm";
|
||||
}
|
||||
|
||||
platformName = osName + "-" + osArch;
|
||||
}
|
||||
|
||||
|
||||
private OsInfo() {
|
||||
}
|
||||
|
||||
public static boolean isLinux() {
|
||||
return osName.startsWith("linux");
|
||||
}
|
||||
|
||||
public static boolean isMac() {
|
||||
return osName.startsWith("mac");
|
||||
}
|
||||
|
||||
public static boolean isWindows() {
|
||||
return osName.startsWith("windows");
|
||||
}
|
||||
|
||||
public static String getPlatformName() {
|
||||
return platformName;
|
||||
}
|
||||
|
||||
public static int getBitness() {
|
||||
return bitness;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
|
||||
/**
|
||||
* The listener that is called when changes to the {@link moodle.sync.core.util.ObservableSet} occur.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public interface SetChangeListener<T extends ObservableSet<?>> {
|
||||
|
||||
/**
|
||||
* Called whenever one or more elements in the Set have changed.
|
||||
*
|
||||
* @param set The changed Set.
|
||||
*/
|
||||
void setChanged(T set);
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.util;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public abstract class ShutdownHandler {
|
||||
|
||||
private final static Logger LOG = LogManager.getLogger(ShutdownHandler.class);
|
||||
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
private final Condition condition = lock.newCondition();
|
||||
|
||||
|
||||
/**
|
||||
* Executes specific code during the shutdown procedure, or throws an exception if unable to do so.
|
||||
*
|
||||
* @return {@code true} to execute next ShutdownHandler, {@code true} to stop the shutdown procedure.
|
||||
*
|
||||
* @throws Exception if unable to execute code.
|
||||
*/
|
||||
abstract public boolean execute() throws Exception;
|
||||
|
||||
|
||||
protected void executeAndWait(Runnable runnable) {
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
runnable.run();
|
||||
|
||||
condition.await();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected void resume() {
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
condition.signal();
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
final protected void logException(Throwable throwable, String throwMessage) {
|
||||
requireNonNull(throwable);
|
||||
requireNonNull(throwMessage);
|
||||
|
||||
LOG.error(throwMessage, throwable);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.view;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
|
||||
/**
|
||||
* Represents an action that takes no arguments and returns no result.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Action {
|
||||
|
||||
/**
|
||||
* Performs this operation.
|
||||
*/
|
||||
void execute();
|
||||
|
||||
/**
|
||||
* Concatenates two given actions. If the first {@code Action} is {@code null},
|
||||
* then the next {@code Action} is returned.
|
||||
*
|
||||
* @param first the first action.
|
||||
* @param next the action to concatenate to the first action.
|
||||
*
|
||||
* @return the concatenated actions.
|
||||
*
|
||||
* @see #andThen(Action)
|
||||
*/
|
||||
static Action concatenate(Action first, Action next) {
|
||||
return isNull(first) ? next : first.andThen(next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a composed {@code Action} that executes, in sequence, this
|
||||
* action followed by the {@code next} action. If performing either
|
||||
* action throws an exception, it is relayed to the caller of the
|
||||
* composed action. If performing this action throws an exception,
|
||||
* the {@code next} action will not be performed.
|
||||
*
|
||||
* @param next the action to perform after this action.
|
||||
*
|
||||
* @return a composed {@code Action} that executes in sequence this
|
||||
* action followed by the {@code next} action.
|
||||
*
|
||||
* @throws NullPointerException if {@code next} is null.
|
||||
*/
|
||||
default Action andThen(Action next) {
|
||||
Objects.requireNonNull(next);
|
||||
|
||||
return () -> {
|
||||
execute();
|
||||
next.execute();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package moodle.sync.core.view;
|
||||
|
||||
/**
|
||||
* Generic notification class used for notification windows with both an accept and decline option.
|
||||
*/
|
||||
public interface ConfirmationNotificationView extends View {
|
||||
|
||||
void setOnConfirm(Action action);
|
||||
|
||||
void setType(NotificationType type);
|
||||
|
||||
void setTitle(String title);
|
||||
|
||||
void setMessage(String message);
|
||||
|
||||
void setOnDiscard(Action action);
|
||||
|
||||
void setConfirmButtonText(String confirmButtonText);
|
||||
|
||||
void setDiscardButtonText(String discardButtonText);
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.view;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
|
||||
/**
|
||||
* Represents an action that accepts a single input argument and returns no
|
||||
* result.
|
||||
*
|
||||
* @param <T> The type of the input to the action.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ConsumerAction<T> {
|
||||
|
||||
/**
|
||||
* Performs this operation on the given argument.
|
||||
*
|
||||
* @param value The input value.
|
||||
*/
|
||||
void execute(T value);
|
||||
|
||||
/**
|
||||
* Concatenates two given consumer actions. If the first {@code ConsumerAction} is
|
||||
* {@code null}, then the next {@code ConsumerAction} is returned.
|
||||
*
|
||||
* @param first the first action.
|
||||
* @param next the action to concatenate to the first action.
|
||||
*
|
||||
* @return the concatenated actions.
|
||||
*
|
||||
* @see #andThen(ConsumerAction)
|
||||
*/
|
||||
static <T> ConsumerAction<T> concatenate(ConsumerAction<T> first, ConsumerAction<T> next) {
|
||||
return isNull(first) ? next : first.andThen(next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a composed {@code ConsumerAction} that executes, in sequence, this
|
||||
* action followed by the {@code next} action. If performing either
|
||||
* action throws an exception, it is relayed to the caller of the
|
||||
* composed action. If performing this action throws an exception,
|
||||
* the {@code next} action will not be performed.
|
||||
*
|
||||
* @param next the action to perform after this action.
|
||||
*
|
||||
* @return a composed {@code ConsumerAction} that executes in sequence this
|
||||
* action followed by the {@code next} action.
|
||||
*
|
||||
* @throws NullPointerException if {@code next} is null.
|
||||
*/
|
||||
default ConsumerAction<T> andThen(ConsumerAction<? super T> next) {
|
||||
Objects.requireNonNull(next);
|
||||
|
||||
return value -> {
|
||||
execute(value);
|
||||
next.execute(value);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.view;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface DirectoryChooserView {
|
||||
|
||||
void setInitialDirectory(File directory);
|
||||
|
||||
void setTitle(String title);
|
||||
|
||||
File show(View parent);
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.view;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface FileChooserView {
|
||||
|
||||
void addExtensionFilter(String description, String... extensions);
|
||||
|
||||
void setInitialDirectory(File directory);
|
||||
|
||||
void setInitialFileName(String name);
|
||||
|
||||
File showOpenFile(View parent);
|
||||
|
||||
File showSaveFile(View parent);
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.view;
|
||||
|
||||
public interface NewVersionView extends NotificationView {
|
||||
|
||||
void setOnClose(Action action);
|
||||
|
||||
void setOnDownload(Action action);
|
||||
|
||||
void setOnOpenUrl(Action action);
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.view;
|
||||
|
||||
public interface NotificationPopupManager {
|
||||
|
||||
void show(View rootView, NotificationPopupView view);
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.view;
|
||||
|
||||
import moodle.sync.core.geometry.Position;
|
||||
|
||||
public interface NotificationPopupView extends NotificationView {
|
||||
|
||||
void setPosition(Position position);
|
||||
|
||||
Position getPosition();
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.view;
|
||||
|
||||
/**
|
||||
* This enum defines the possible types of a notification.
|
||||
*
|
||||
* @author Alex Andres
|
||||
*/
|
||||
public enum NotificationType {
|
||||
|
||||
/** Default notification. */
|
||||
DEFAULT,
|
||||
|
||||
/** Error notification with specific error icon. */
|
||||
ERROR,
|
||||
|
||||
/** Question notification with specific question icon. */
|
||||
QUESTION,
|
||||
|
||||
/** Warning notification with specific warning icon. */
|
||||
WARNING,
|
||||
|
||||
/** Waiting notification showing a waiting indicator. */
|
||||
WAITING;
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2020 TU Darmstadt, Department of Computer Science,
|
||||
* Embedded Systems and Applications Group.
|
||||
*
|
||||
* 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 moodle.sync.core.view;
|
||||
|
||||
public interface NotificationView extends View {
|
||||
|
||||
void setType(NotificationType type);
|
||||
|
||||
void setTitle(String title);
|
||||
|
||||
void setMessage(String message);
|
||||
|
||||
void setOnClose(Action action);
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue