diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 00000000..3f0c5100 --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,81 @@ +# This workflow will build and test all Gradle-migrated projects in specs-java-libs every day at midnight +# It will also run on every push and pull request + +name: nightly + +on: + push: + workflow_dispatch: + +permissions: + checks: write + contents: write + +env: + JAVA_VERSION: 17 + +jobs: + build-gradle-projects: + name: Build & Test Gradle Projects Sequentially + runs-on: ubuntu-latest + steps: + - name: Checkout specs-java-libs + uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: ${{ env.JAVA_VERSION }} + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + gradle-version: current + dependency-graph: generate-and-submit + + - name: Build and test all Gradle projects sequentially + run: | + projects=( + AsmParser + CommonsCompressPlus + CommonsLangPlus + GearmanPlus + GitPlus + Gprofer + GsonPlus + GuiHelper + JacksonPlus + JadxPlus + JavaGenerator + jOptions + JsEngine + LogbackPlus + SpecsUtils + SymjaPlus + tdrcLibrary + XStreamPlus + ) + failed=() + for project in "${projects[@]}"; do + echo "\n===== Building and testing $project =====" + cd "$project" + if ! gradle build test; then + echo "[ERROR] $project failed to build or test" + failed+=("$project") + fi + cd - + done + if [ ${#failed[@]} -ne 0 ]; then + echo "\nThe following projects failed: ${failed[*]}" + exit 1 + fi + env: + GITHUB_DEPENDENCY_GRAPH_ENABLED: false + + - name: Publish Test Reports + uses: mikepenz/action-junit-report@v4 + if: always() + with: + report_paths: '**/build/test-results/test/TEST-*.xml' + summary: true diff --git a/CommonsCompressPlus/src/pt/up/fe/specs/compress/ZipFormat.java b/CommonsCompressPlus/src/pt/up/fe/specs/compress/ZipFormat.java index af49c55f..7f2db5c5 100644 --- a/CommonsCompressPlus/src/pt/up/fe/specs/compress/ZipFormat.java +++ b/CommonsCompressPlus/src/pt/up/fe/specs/compress/ZipFormat.java @@ -1,14 +1,14 @@ /** * Copyright 2017 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.compress; @@ -26,28 +26,59 @@ import pt.up.fe.specs.util.lazy.Lazy; import pt.up.fe.specs.util.providers.StringProvider; +/** + * Enum representing supported compression formats for file output. + *

+ * Provides methods to create compressors for ZIP and GZ formats and to retrieve formats by extension. + */ public enum ZipFormat implements StringProvider { + /** ZIP file format. */ ZIP("zip"), + /** GZ (GZip) file format. */ GZ("gz"); private static final Lazy> ENUM_HELPER = EnumHelperWithValue.newLazyHelperWithValue(ZipFormat.class); private final String extension; + /** + * Creates a new ZipFormat with the given file extension. + * + * @param extension the file extension for the format + */ private ZipFormat(String extension) { this.extension = extension; } + /** + * Returns an Optional containing the ZipFormat corresponding to the given extension, if available. + * + * @param extension the file extension + * @return an Optional with the matching ZipFormat, or empty if not found + */ public static Optional fromExtension(String extension) { return ENUM_HELPER.get().fromValueTry(extension); } + /** + * Returns the string representation (file extension) of this format. + * + * @return the file extension + */ @Override public String getString() { return extension; } + /** + * Creates a new file compressor OutputStream for the given filename and output stream, according to this format. + * + * @param filename the name of the file to compress (used for ZIP entries) + * @param outputStream the output stream to wrap + * @return a new OutputStream for the compressed file + * @throws RuntimeException if the compressor cannot be created + */ public OutputStream newFileCompressor(String filename, OutputStream outputStream) { switch (this) { case ZIP: diff --git a/CommonsLangPlus/README.md b/CommonsLangPlus/README.md new file mode 100644 index 00000000..09fc6fca --- /dev/null +++ b/CommonsLangPlus/README.md @@ -0,0 +1,14 @@ +# CommonsLangPlus + +CommonsLangPlus is a utility library that provides additional wrappers and helpers around the Apache Commons Lang and Commons Text libraries. It offers platform detection utilities and string manipulation helpers to simplify common Java development tasks. + +## Features +- Platform detection (Windows, Linux, Mac, Unix, ARM, CentOS) +- String escaping utilities (HTML, etc.) +- Lightweight and easy to use + +## Usage +Add CommonsLangPlus to your Java project and use the static utility methods for platform checks and string operations. + +## License +This project is licensed under the Apache License 2.0. diff --git a/CommonsLangPlus/src/pt/up/fe/specs/lang/ApacheStrings.java b/CommonsLangPlus/src/pt/up/fe/specs/lang/ApacheStrings.java index 6459c691..3619082d 100644 --- a/CommonsLangPlus/src/pt/up/fe/specs/lang/ApacheStrings.java +++ b/CommonsLangPlus/src/pt/up/fe/specs/lang/ApacheStrings.java @@ -1,22 +1,31 @@ /** * Copyright 2017 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.lang; import org.apache.commons.text.StringEscapeUtils; +/** + * Utility class for Apache Commons Text string operations. + */ public class ApacheStrings { + /** + * Escapes HTML entities in the given string using Apache Commons Text. + * + * @param html the input HTML string + * @return the escaped HTML string + */ public static String escapeHtml(String html) { return StringEscapeUtils.escapeHtml4(html); } diff --git a/CommonsLangPlus/src/pt/up/fe/specs/lang/SpecsPlatforms.java b/CommonsLangPlus/src/pt/up/fe/specs/lang/SpecsPlatforms.java index 3514de1a..a0e04100 100644 --- a/CommonsLangPlus/src/pt/up/fe/specs/lang/SpecsPlatforms.java +++ b/CommonsLangPlus/src/pt/up/fe/specs/lang/SpecsPlatforms.java @@ -8,7 +8,7 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.lang; @@ -16,18 +16,20 @@ import org.apache.commons.lang3.SystemUtils; /** - * Wrappers around Apache commons-lang utility methods related to system platform identification. - * + * Utility class providing wrappers around Apache commons-lang methods for system platform identification. *

+ * Includes methods to check the current operating system and platform details. + * * TODO: Rename to ApachePlatforms * * @author JoaoBispo - * */ public class SpecsPlatforms { /** * Returns true if the operating system is a form of Windows. + * + * @return true if Windows OS, false otherwise */ public static boolean isWindows() { return SystemUtils.IS_OS_WINDOWS; @@ -35,61 +37,57 @@ public static boolean isWindows() { /** * Returns true if the operating system is a form of Linux. + * + * @return true if Linux OS, false otherwise */ public static boolean isLinux() { return SystemUtils.IS_OS_LINUX; } /** - * Returns true if the operating system is a form of Linux. + * Returns true if the operating system is Linux running on ARM architecture. + * + * @return true if Linux ARM, false otherwise */ public static boolean isLinuxArm() { return SystemUtils.IS_OS_LINUX && "arm".equals(System.getProperty("os.arch").toLowerCase()); } + /** + * Returns true if the operating system version indicates CentOS 6. + * + * @return true if CentOS 6, false otherwise + */ public static boolean isCentos6() { return System.getProperty("os.version").contains(".el6."); } + /** + * Returns the name of the current platform/OS. + * + * @return the OS name + */ public static String getPlatformName() { return SystemUtils.OS_NAME; } /** - * Returns true if the operating system is a form of Linux or Solaris. + * Returns true if the operating system is a form of Unix (Linux or Solaris). + * + * @return true if Unix OS, false otherwise */ public static boolean isUnix() { return SystemUtils.IS_OS_UNIX; } + /** + * Returns true if the operating system is a form of Mac OS. + * + * @return true if Mac OS, false otherwise + */ public static boolean isMac() { return SystemUtils.IS_OS_MAC; } - /* - public static Process getShell() { - String cmd = getShellCommand(); - - ProcessBuilder builder = new ProcessBuilder(cmd); - - try { - return builder.start(); - } catch (IOException e) { - throw new RuntimeException("Could not start process " + cmd); - } - } - - public static String getShellCommand() { - if (isWindows()) { - return "cmd.exe /start"; - } - - if (isUnix()) { - return "/bin/bash"; - } - - throw new RuntimeException("No shell defined for platform " + getPlatformName()); - } - */ - + // TODO: Implement shell-related utilities if needed in the future. } diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/GearmanUtils.java b/GearmanPlus/src/pt/up/fe/specs/gearman/GearmanUtils.java index 8a091d6d..6142536d 100644 --- a/GearmanPlus/src/pt/up/fe/specs/gearman/GearmanUtils.java +++ b/GearmanPlus/src/pt/up/fe/specs/gearman/GearmanUtils.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.gearman; @@ -20,35 +20,44 @@ import pt.up.fe.specs.util.SpecsLogs; +/** + * Utility methods for creating and managing Gearman servers. + */ public class GearmanUtils { /** - * Creates a Gearman server on default port is 4730. - * - * @param gearman - * @param args - * @return + * Creates a Gearman server on the default port (4730) or connects to a remote server if an address is provided. + * + * @param gearman the Gearman instance + * @param args if not empty, the first element is used as the server address + * @return a GearmanServer instance + * @throws RuntimeException if the server cannot be started or connected */ public static GearmanServer newServer(final Gearman gearman, String[] args) { return newServer(gearman, 4730, args); } + /** + * Creates a Gearman server on the specified port or connects to a remote server if an address is provided. + * + * @param gearman the Gearman instance + * @param port the port to use + * @param args if not empty, the first element is used as the server address + * @return a GearmanServer instance + * @throws RuntimeException if the server cannot be started or connected + */ public static GearmanServer newServer(final Gearman gearman, int port, String[] args) { - try { if (args.length > 0) { String addr = args[0]; SpecsLogs.msgInfo("Connecting to Gearman Server on " + addr + ":" + port); return gearman.createGearmanServer(addr, port); } - SpecsLogs.msgInfo("Gearman Server listening on port " + port); return gearman.startGearmanServer(port); - } catch (IOException e) { throw new RuntimeException("Exception while trying to start Gearman Server:\n", e); } - } } diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/GenericSpecsWorker.java b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/GenericSpecsWorker.java index b297a582..208234ce 100644 --- a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/GenericSpecsWorker.java +++ b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/GenericSpecsWorker.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.gearman.specsworker; @@ -21,6 +21,12 @@ import com.google.gson.GsonBuilder; +/** + * Generic Gearman worker that delegates work to a provided function and builds error output using a function. + *

+ * This worker allows dynamic delegation of Gearman jobs to a user-supplied {@link GearmanFunction} and custom error output + * formatting using a {@link Function}. + */ public class GenericSpecsWorker extends SpecsWorker { private final GearmanFunction function; @@ -28,36 +34,70 @@ public class GenericSpecsWorker extends SpecsWorker { private final String workerName; + /** + * Constructs a new GenericSpecsWorker. + * + * @param workerName the name of the worker + * @param function the Gearman function to delegate work to + * @param outputBuilder a function to build error output objects from error messages + * @param timeout the timeout value + * @param timeUnit the time unit for the timeout + */ public GenericSpecsWorker(String workerName, GearmanFunction function, Function outputBuilder, long timeout, TimeUnit timeUnit) { super(timeout, timeUnit); - this.workerName = workerName; this.function = function; this.outputBuilder = outputBuilder; } + /** + * Returns the name of this worker. + * + * @return the worker name + */ @Override public String getWorkerName() { return workerName; } + /** + * Setup hook called before work execution. Default implementation does nothing. + */ @Override public void setUp() { - + // No setup required by default } + /** + * Teardown hook called after work execution. Default implementation does nothing. + */ @Override public void tearDown() { - + // No teardown required by default } + /** + * Delegates the work to the provided Gearman function. + * + * @param function the function name + * @param data the input data + * @param callback the Gearman callback + * @return the result of the delegated function + * @throws Exception if the delegated function throws an exception + */ @Override public byte[] workInternal(String function, byte[] data, GearmanFunctionCallback callback) throws Exception { return this.function.work(function, data, callback); } + /** + * Builds the error output using the provided outputBuilder function and serializes it as JSON. + * + * @param message the error message + * @return the error output as JSON bytes + */ @Override protected byte[] getErrorOutput(String message) { return new GsonBuilder().create().toJson(outputBuilder.apply(message)).getBytes(); diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/JsonSpecsWorker.java b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/JsonSpecsWorker.java index b452addc..f7dee337 100644 --- a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/JsonSpecsWorker.java +++ b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/JsonSpecsWorker.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.gearman.specsworker; @@ -24,45 +24,76 @@ import pt.up.fe.specs.util.SpecsLogs; +/** + * Abstract Gearman worker that handles JSON input/output using Gson. + * + * @param the input type + * @param the output type + */ public abstract class JsonSpecsWorker extends SpecsWorker { private final Gson gson; private final Class inputClass; - // private final Class outputClass; - // public TypedSpecsWorker(Class inputClass, Class outputClass, long timeout, TimeUnit timeUnit) { + /** + * Constructs a JsonSpecsWorker with the given input class and timeout settings. + * + * @param inputClass the class of the input type + * @param timeout the timeout duration + * @param timeUnit the time unit for the timeout + */ public JsonSpecsWorker(Class inputClass, long timeout, TimeUnit timeUnit) { super(timeout, timeUnit); - this.inputClass = inputClass; - // this.outputClass = outputClass; this.gson = new GsonBuilder().create(); } + /** + * Handles the work by deserializing input, invoking the worker, and serializing the result as JSON. + * + * @param function the function name + * @param data the input data as bytes + * @param callback the Gearman callback + * @return the result as JSON bytes + * @throws Exception if processing fails + */ @Override public byte[] workInternal(String function, byte[] data, GearmanFunctionCallback callback) throws Exception { - - // Type typeOfT = new TypeToken() { - // }.getType(); - - // I parsedData = gson.fromJson(new String(data), typeOfT); I parsedData = gson.fromJson(new String(data), inputClass); O result = workInternal(function, parsedData, callback); - // Print time-stamp var time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); SpecsLogs.info("Finished job '" + this.getClass().getName() + "' at " + time); - return gson.toJson(result).getBytes(); } + /** + * Performs the actual work using the deserialized input. + * + * @param function the function name + * @param data the input data + * @param callback the Gearman callback + * @return the result + */ public abstract O workInternal(String function, I data, GearmanFunctionCallback callback); + /** + * Returns the error output as JSON bytes. + * + * @param message the error message + * @return the error output as JSON bytes + */ @Override protected byte[] getErrorOutput(String message) { return gson.toJson(getTypedErrorOutput(message)).getBytes(); } + /** + * Returns a typed error output object for the given message. + * + * @param message the error message + * @return the error output object + */ protected abstract O getTypedErrorOutput(String message); } diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/SpecsWorker.java b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/SpecsWorker.java index 03c26e34..345fc663 100644 --- a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/SpecsWorker.java +++ b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/SpecsWorker.java @@ -1,14 +1,14 @@ /** * Copyright 2013 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.gearman.specsworker; @@ -30,116 +30,109 @@ import pt.up.fe.specs.util.SpecsStrings; /** + * Abstract base class for Gearman workers with timeout, setup/teardown, and error handling support. + *

+ * Provides a framework for running Gearman jobs with configurable timeouts, setup/teardown hooks, and error reporting. + * Subclasses should implement {@link #workInternal(String, byte[], GearmanFunctionCallback)} and + * {@link #getErrorOutput(String)} for custom job logic and error output. + * * @author Joao Bispo - * */ public abstract class SpecsWorker implements GearmanFunction { private final long timeout; private final TimeUnit timeUnit; + /** + * Constructs a SpecsWorker with the given timeout and time unit. + * + * @param timeout the timeout value for job execution + * @param timeUnit the time unit for the timeout + */ public SpecsWorker(long timeout, TimeUnit timeUnit) { - this.timeout = timeout; this.timeUnit = timeUnit; } - // public SpecsWorker() { - // this(SPeCSGearman.getTimeout(), SPeCSGearman.getTimeunit()); - // } - /** - * @return the timeout + * Returns the timeout value for job execution. + * + * @return the timeout value */ public long getTimeout() { return timeout; } /** - * @return the timeUnit + * Returns the time unit for the timeout value. + * + * @return the time unit */ public TimeUnit getTimeUnit() { return timeUnit; } /** - * As default, returns the simple name of the class. - * - * @return + * Returns the name of this worker. By default, returns the simple class name. + * + * @return the worker name */ public String getWorkerName() { return getClass().getSimpleName(); } - /* (non-Javadoc) - * @see org.gearman.GearmanFunction#work(java.lang.String, byte[], org.gearman.GearmanFunctionCallback) + /** + * Executes the Gearman job, calling setup and teardown hooks. + * + * @param function the function name + * @param data the input data + * @param callback the Gearman callback + * @return the result of the job + * @throws Exception if job execution fails */ @Override public byte[] work(String function, byte[] data, GearmanFunctionCallback callback) throws Exception { - - // if (name.endsWith("WithTimeout")) { - // name = name.substring(0, name.length() - "WithTimeout".length()); - // } - - // long executeStart = System.nanoTime(); - // SetUp setUp(); - // LoggingUtils.msgInfo(ParseUtils.takeTime("[" + name + "] Setup: ", tic)); - byte[] result = execute(function, data, callback); - - // LoggingUtils.msgInfo(ParseUtils.takeTime("[" + name + "] Execution: ", executeStart)); - - // TearDown - // tic = System.nanoTime(); tearDown(); - - // LoggingUtils.msgInfo(ParseUtils.takeTime("[" + name + "] Teardown: ", tic)); - // LoggingUtils.msgInfo(ParseUtils.takeTime("[" + name + "] Total: ", workStart)); - return result; } /** - * @param function - * @param data - * @param callback - * @return - * @throws InterruptedException - * @throws ExecutionException + * Executes the job with timeout and error handling, writing input/output to files if outputDir is set. + * + * @param function the function name + * @param data the input data + * @param callback the Gearman callback + * @return the result of the job + * @throws InterruptedException if execution is interrupted + * @throws ExecutionException if execution fails */ private byte[] execute(String function, byte[] data, GearmanFunctionCallback callback) throws InterruptedException, ExecutionException { - File outputDir = getOutputDir(); - - // Write input data, for debug if (outputDir != null) { SpecsIo.write(new File(outputDir, "input_data.json"), new String(data)); } - byte[] result = executeInternal(function, data, callback); - - // Write output data, for debug if (outputDir != null) { SpecsIo.write(new File(outputDir, "output_data.json"), new String(result)); } - - // Create timeout output - // if (result == null) { - // result = getErrorOutput(); - // } return result; } + /** + * Executes the job in a separate thread, enforcing the timeout and handling exceptions. + * + * @param function the function name + * @param data the input data + * @param callback the Gearman callback + * @return the result of the job + */ public byte[] executeInternal(String function, byte[] data, GearmanFunctionCallback callback) { - // Launch work in another thread ExecutorService executor = Executors.newSingleThreadExecutor(); - - // Future future = executor.submit(new Task(gearmanFunction, data, callback)); TaskV2 task = new TaskV2(this, function, data, callback); - // Future future = executor.submit(new Task(function, data, callback)); Future future = executor.submit(task); byte[] result = null; try { @@ -147,114 +140,108 @@ public byte[] executeInternal(String function, byte[] data, GearmanFunctionCallb String id = callback != null ? new String(callback.getJobHandle()) : ""; SpecsLogs.msgInfo("[SpecsWorker] Starting '" + name + "' (" + id + " -> " + SpecsIo.getWorkingDir().getAbsolutePath() + ")"); - - // LoggingUtils.msgLib("Started task with a timeout of " + timeout + " " + timeUnit); long workStart = System.nanoTime(); result = future.get(timeout, timeUnit); long workEnd = System.nanoTime(); - // LoggingUtils.msgLib("Finished task in the alloted time"); SpecsLogs.msgInfo("[SpecsWorker] Finished '" + getWorkerName() + "', " + SpecsStrings.parseTime(workEnd - workStart) + " (id " + id + ")"); } catch (TimeoutException e) { SpecsLogs.warn("[SpecsWorker] Timeout during worker execution", e); - // future.cancel(true); future.cancel(true); SpecsLogs.msgInfo("Worker [" + Thread.currentThread().getName() + "]: putting thread/task to sleep... "); task.interrupt(); - // executor.shutdownNow(); - // return getErrorOutput(getTimeoutMessage()); result = getErrorOutput(getTimeoutMessage()); } catch (Exception e) { SpecsLogs.warn("[SpecsWorker] Exception during worker execution", e); future.cancel(true); task.interrupt(); - // executor.shutdownNow(); - // return getErrorOutput(e.getMessage()); result = getErrorOutput(e.getMessage()); - // return getErrorOutput(); } - executor.shutdownNow(); return result; } + /** + * Returns a timeout message for error reporting. + * + * @return the timeout message + */ private String getTimeoutMessage() { return "Terminated task after exceeding the maximum alloted time of " + getTimeout() + SpecsStrings.getTimeUnitSymbol(getTimeUnit()); } /** - * Executes before the main work. Default implementation does nothing. + * Setup hook called before job execution. Default implementation does nothing. */ public void setUp() { - + // No setup required by default } - /* - public void sleep() { - try { - SpecsLogs.msgInfo("Task [" + Thread.currentThread().getName() + "] Have been put to sleep... "); - Thread.sleep(2000); - SpecsLogs.msgInfo("Task [" + Thread.currentThread().getName() + "] Awake!"); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); // set interrupt flag - SpecsLogs.warn("Interrupted:\n", e); - } - } - */ - /** - * Executes after the main work. Default implementation does nothing. + * Teardown hook called after job execution. Default implementation does nothing. */ public void tearDown() { - + // No teardown required by default } + /** + * Performs the actual work for the Gearman job. Must be implemented by subclasses. + * + * @param function the function name + * @param data the input data + * @param callback the Gearman callback + * @return the result of the job + * @throws Exception if job execution fails + */ public abstract byte[] workInternal(String function, byte[] data, GearmanFunctionCallback callback) throws Exception; + /** + * Returns the error output for a given error message. Must be implemented by subclasses. + * + * @param message the error message + * @return the error output as bytes + */ protected abstract byte[] getErrorOutput(String message); + /** + * Task for executing a Gearman job in a separate thread. + */ class Task implements Callable { - private final String function; private final byte[] data; private final GearmanFunctionCallback callback; /** - * @param gearmanFunction - * @param data - * @param callback + * Constructs a Task for the given function, data, and callback. + * + * @param function the function name + * @param data the input data + * @param callback the Gearman callback */ - /* - public Task(String gearmanFunction, byte[] data, GearmanFunctionCallback callback) { - this.function = gearmanFunction; - this.data = data; - this.callback = callback; - } - */ - public Task(String function, byte[] data, GearmanFunctionCallback callback) { - this.function = function; this.data = data; this.callback = callback; - } + /** + * Calls the workInternal method to perform the job. + * + * @return the result of the job + * @throws Exception if job execution fails + */ @Override public byte[] call() throws Exception { - // return workInternal(gearmanFunction, data, callback); return workInternal(function, data, callback); } - - // public static void sleep() { - // Thread.sleep(2000); - // } } + /** + * TaskV2 for executing a Gearman job in a separate thread, with thread interruption support. + */ static class TaskV2 implements Callable { - private final SpecsWorker worker; private final String function; private final byte[] data; @@ -262,26 +249,26 @@ static class TaskV2 implements Callable { private Thread taskThread = null; /** - * @param gearmanFunction - * @param data - * @param callback + * Constructs a TaskV2 for the given worker, function, data, and callback. + * + * @param worker the SpecsWorker instance + * @param function the function name + * @param data the input data + * @param callback the Gearman callback */ - /* - public Task(String gearmanFunction, byte[] data, GearmanFunctionCallback callback) { - this.function = gearmanFunction; - this.data = data; - this.callback = callback; - } - */ - public TaskV2(SpecsWorker worker, String function, byte[] data, GearmanFunctionCallback callback) { this.worker = worker; this.function = function; this.data = data; this.callback = callback; - } + /** + * Calls the worker's workInternal method to perform the job, tracking the thread for interruption. + * + * @return the result of the job + * @throws Exception if job execution fails + */ @Override public byte[] call() throws Exception { taskThread = Thread.currentThread(); @@ -290,20 +277,27 @@ public byte[] call() throws Exception { SpecsLogs.msgInfo("Finished task in thread " + taskThread.getName()); taskThread = null; return result; - // return workInternal(gearmanFunction, data, callback); - // return workInternal(function, data, callback); } + /** + * Returns the SpecsWorker associated with this task. + * + * @return the SpecsWorker instance + */ public SpecsWorker getWorker() { return worker; } + /** + * Interrupts the running thread for this task, waiting 2 seconds for cleanup. + * + * If the thread is still alive after interruption, logs a warning. + */ public void interrupt() { if (taskThread == null) { SpecsLogs.msgInfo("Task.sleep(): No thread set, returning"); return; } - SpecsLogs.msgInfo("Interrupting task in thread " + taskThread.getName() + ", waiting 2 seconds"); taskThread.interrupt(); try { @@ -321,21 +315,16 @@ public void interrupt() { // https://stackoverflow.com/questions/24855182/interrupt-java-thread-running-nashorn-script# taskThread.stop(); } else { - SpecsLogs.msgInfo("Thread " + taskThread.getName() + "died gracefully"); + SpecsLogs.msgInfo("Thread " + taskThread.getName() + " died gracefully"); } - - /* - try { - SpecsLogs.msgInfo("Task in thread " + taskThread.getName() + " awake!"); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); // set interrupt flag - SpecsLogs.warn("Interrupted:\n", e); - } - */ } - } + /** + * Returns the output directory for input/output debug files. Default is null (no output). + * + * @return the output directory, or null if not set + */ public File getOutputDir() { return null; } diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/utils/GearmanSecurityManager.java b/GearmanPlus/src/pt/up/fe/specs/gearman/utils/GearmanSecurityManager.java index a3c6f35f..fe3abd1a 100644 --- a/GearmanPlus/src/pt/up/fe/specs/gearman/utils/GearmanSecurityManager.java +++ b/GearmanPlus/src/pt/up/fe/specs/gearman/utils/GearmanSecurityManager.java @@ -1,36 +1,54 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.gearman.utils; import java.security.Permission; +/** + * Security manager for Gearman workers that blocks System.exit() calls and allows all other permissions. + *

+ * This class is intended to prevent accidental JVM termination by capturing calls to System.exit(). + * All other permission checks are allowed. + */ public class GearmanSecurityManager extends SecurityManager { + /** + * Allows all permissions (no restrictions). + */ @Override public void checkPermission(Permission perm) { - // allow anything. + // allow anything. } + /** + * Allows all permissions (no restrictions). + */ @Override public void checkPermission(Permission perm, Object context) { - // allow anything. + // allow anything. } + /** + * Throws a SecurityException when System.exit() is called, preventing JVM termination. + * + * @param status the exit status + * @throws SecurityException always + */ @Override public void checkExit(int status) { - super.checkExit(status); - throw new SecurityException("Captured call to System.exit(" + status + ")"); + super.checkExit(status); + throw new SecurityException("Captured call to System.exit(" + status + ")"); } } diff --git a/GitPlus/README.md b/GitPlus/README.md new file mode 100644 index 00000000..141fbe98 --- /dev/null +++ b/GitPlus/README.md @@ -0,0 +1,15 @@ +# GitPlus + +GitPlus is a Java library that provides utility classes and methods for interacting with Git repositories using the JGit library. It simplifies common Git operations such as cloning, checking out branches or commits, and managing multiple repositories programmatically. + +## Features +- Clone and manage multiple Git repositories +- Checkout branches and commits +- Utility methods for repository folder management +- Enum-based URL options for advanced repository handling + +## Usage +Add GitPlus to your Java project and use the provided static utility methods and classes for Git operations. + +## License +This project is licensed under the Apache License 2.0. diff --git a/GitPlus/src/pt/up/fe/specs/git/GitRepo.java b/GitPlus/src/pt/up/fe/specs/git/GitRepo.java index 73eb0341..21af0f4f 100644 --- a/GitPlus/src/pt/up/fe/specs/git/GitRepo.java +++ b/GitPlus/src/pt/up/fe/specs/git/GitRepo.java @@ -1,14 +1,14 @@ /** * Copyright 2022 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.git; @@ -28,6 +28,10 @@ import pt.up.fe.specs.util.SpecsIo; import pt.up.fe.specs.util.SpecsLogs; +/** + * Represents a local clone of a remote Git repository, with support for options such as commit and folder selection. + * Provides methods for preparing, cloning, and updating the repository. + */ public class GitRepo { private final Map options; @@ -35,6 +39,12 @@ public class GitRepo { private final File repoFolder; private final File workFolder; + /** + * Creates a new GitRepo instance with the given repository URL and options. + * + * @param repoUrl the URL of the remote repository + * @param options a map of options for the repository, such as commit and folder + */ public GitRepo(String repoUrl, Map options) { this.repoUrl = repoUrl; this.options = options; @@ -52,6 +62,11 @@ public GitRepo(String repoUrl, Map options) { this.workFolder = createWorkFolder(); } + /** + * Creates the working folder for the repository based on the options provided. + * + * @return the working folder + */ private File createWorkFolder() { // Check folder option var foldername = options.get(GitUrlOption.FOLDER); @@ -71,10 +86,21 @@ private File createWorkFolder() { return workFolder; } + /** + * Creates a new GitRepo instance with the given repository URL and no options. + * + * @param repoUrl the URL of the remote repository + */ public GitRepo(String repoUrl) { this(repoUrl, Collections.emptyMap()); } + /** + * Creates a new GitRepo instance from a repository path. + * + * @param repositoryPath the path to the repository + * @return a new GitRepo instance + */ public static GitRepo newInstance(String repositoryPath) { SpecsLogs.msgInfo("Processing git url '" + repositoryPath + "'"); @@ -116,6 +142,11 @@ public File getWorkFolder() { return workFolder; } + /** + * Creates the folder for the repository based on its name and options. + * + * @return the repository folder + */ private File createRepoFolder() { // Get repo name @@ -133,6 +164,12 @@ private File createRepoFolder() { return new File(eclipseBuildFolder, repoName); } + /** + * Parses the repository URL and extracts options. + * + * @param repositoryPath the repository URL + * @return a map of options extracted from the URL + */ private static Map parseUrl(String repositoryPath) { var urlStringOptions = SpecsIo.parseUrl(repositoryPath) .map(url -> SpecsIo.parseUrlQuery(url)) @@ -170,11 +207,6 @@ private void prepareRepo() { // Cloning repo from scratch, no pull needed clonedRepo = true; - // Check if there is a login/pass in the url - // CredentialsProvider cp = getCredentials(repositoryPath); - // System.out.println("SETTING NULL"); - // clone.setCredentialsProvider(null); - // clone.call(); } catch (GitAPIException e) { throw new RuntimeException("Could not clone repository '" + repoUrl + "'", e); } @@ -205,14 +237,6 @@ private void prepareRepo() { // If the repo was just cloned, checkout the commit if (clonedRepo) { - // First, fetch branches - // SpecsLogs.info("Fetching branches"); - // var fetchResult = repo.fetch() - // .setRefSpecs(new RefSpec("refs/heads/" + branch)) - // .call(); - - // System.out.println("FETCH RESULT: " + fetchResult.getTrackingRefUpdates()); - // Only checkout if not in the correct branch String currentBranch = getCurrentBranch(repo); @@ -256,69 +280,14 @@ private void prepareRepo() { if (isBranchName) { pull(repo, commit); } - /* - // If branch, checkout branch - - var branch = urlOptions.get(GitUrlOption.BRANCH); - - String currentBranch = null; - try { - currentBranch = repo.getRepository().getBranch(); - } catch (IOException e) { - throw new RuntimeException("Could not get current branch", e); - } - - if (branch != null) { - try { - - // First, fetch branches - // SpecsLogs.info("Fetching branches"); - // var fetchResult = repo.fetch() - // .setRefSpecs(new RefSpec("refs/heads/" + branch)) - // .call(); - - // System.out.println("FETCH RESULT: " + fetchResult.getTrackingRefUpdates()); - - // Only checkout if not in the correct branch - if (!branch.equals(currentBranch)) { - SpecsLogs.msgInfo("Checking out branch '" + branch + "'"); - repo.checkout() - .setCreateBranch(true) - .setName(branch) - // .setStartPoint(commit) - .call(); - } else { - SpecsLogs.msgInfo("Already in branch '" + branch + "'"); - } - } catch (GitAPIException e) { - throw new RuntimeException( - "Could not checkout branch '" + branch + "' in folder '" + repoFolder.getAbsolutePath() + "'", - e); - } - } - - var commit = urlOptions.get(GitUrlOption.COMMIT); - if (commit != null) { - - try { - repo.checkout() - .setCreateBranch(true) - .setName(commit) - // .setStartPoint(commit) - .call(); - } catch (GitAPIException e) { - throw new RuntimeException( - "Could not checkout commit '" + commit + "' in folder '" + repoFolder.getAbsolutePath() + "'", - e); - } - // (new RevWalk(repo, 0).parseCommit(null) - } - - return needsPull; - */ - } + /** + * Gets the current branch of the repository. + * + * @param repo the Git repository + * @return the name of the current branch + */ private String getCurrentBranch(Git repo) { String currentBranch = null; try { @@ -329,6 +298,9 @@ private String getCurrentBranch(Git repo) { return currentBranch; } + /** + * Checks for problems in the repository folder and cleans it if necessary. + */ private void checkRepoProblems() { // If folder only contains a single .git folder, something might have gone wrong, delete folder @@ -342,11 +314,10 @@ private void checkRepoProblems() { } /** - * TODO: cleanRepoUrl here is needed because getRepoFolder receives the Query params, to avoid parsing them again. - * If there is a class just for a single repo url, this could be managed differently. - * - * @param cleanRepoUrl - * @param repo + * Pulls the latest changes from the remote repository. + * + * @param repo the Git repository + * @param branch the branch to pull, or null for the default branch */ private void pull(Git repo, String branch) { try { diff --git a/GitPlus/src/pt/up/fe/specs/git/GitRepos.java b/GitPlus/src/pt/up/fe/specs/git/GitRepos.java index e2bb9bfa..8d28da93 100644 --- a/GitPlus/src/pt/up/fe/specs/git/GitRepos.java +++ b/GitPlus/src/pt/up/fe/specs/git/GitRepos.java @@ -1,14 +1,14 @@ /** * Copyright 2017 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.git; @@ -18,21 +18,26 @@ import java.util.concurrent.ConcurrentHashMap; /** + * Utility class for managing multiple Git repositories within the JVM. + * Provides methods to retrieve and cache repository instances by path. + * * TODO: Candidate for using GitManager. - * - * @author Joao Bispo * + * @author Joao Bispo */ public class GitRepos { - // Maps repo names to the folder where they are in the system - // One instance per JVM + /** + * Maps repo names to the folder where they are in the system. One instance per JVM. + */ private static final Map REPOS = new ConcurrentHashMap<>(); - // public GitRepos() { - // - // } - + /** + * Returns a {@link GitRepo} instance for the given repository path. If not cached, creates and caches a new instance. + * + * @param repositoryPath the path to the repository + * @return the GitRepo instance + */ public static GitRepo getRepo(String repositoryPath) { var repo = REPOS.get(repositoryPath); @@ -44,19 +49,13 @@ public static GitRepo getRepo(String repositoryPath) { return repo; } + /** + * Returns the working folder for the given repository path. + * + * @param repositoryPath the path to the repository + * @return the working folder as a File + */ public File getFolder(String repositoryPath) { return getRepo(repositoryPath).getWorkFolder(); - // Get repo name - // String repoName = SpecsGit.getRepoName(repositoryPath); - - // Check if it is already in the map - // File repoFolder = repos.get(repoName); - // var repo = REPOS.get(repositoryPath); - // if (repo == null) { - // repo = GitRepo.newInstance(repositoryPath); - // REPOS.put(repositoryPath, repo); - // } - // - // return repoFolder; } } diff --git a/GitPlus/src/pt/up/fe/specs/git/GitUrlOption.java b/GitPlus/src/pt/up/fe/specs/git/GitUrlOption.java index bf3ccba5..25d1faf3 100644 --- a/GitPlus/src/pt/up/fe/specs/git/GitUrlOption.java +++ b/GitPlus/src/pt/up/fe/specs/git/GitUrlOption.java @@ -1,28 +1,30 @@ /** * Copyright 2022 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.git; import pt.up.fe.specs.util.providers.StringProvider; +/** + * Enum representing options that can be specified in a Git repository URL. + */ public enum GitUrlOption implements StringProvider { - // BRANCH, /** - * A commit or branch that should be used + * A commit or branch that should be used. */ COMMIT, /** - * A folder inside the repository + * A folder inside the repository. */ FOLDER; @@ -32,9 +34,13 @@ private GitUrlOption() { this.option = name().toLowerCase(); } + /** + * Returns the string representation of the option. + * + * @return the option as a string + */ @Override public String getString() { return option; } - } diff --git a/GitPlus/src/pt/up/fe/specs/git/SpecsGit.java b/GitPlus/src/pt/up/fe/specs/git/SpecsGit.java index e7bf3bcb..c1ce6d9e 100644 --- a/GitPlus/src/pt/up/fe/specs/git/SpecsGit.java +++ b/GitPlus/src/pt/up/fe/specs/git/SpecsGit.java @@ -40,9 +40,10 @@ import pt.up.fe.specs.util.SpecsLogs; /** + * Utility class for Git operations using JGit. + * Provides methods for cloning, checking out, and managing Git repositories. * * @author JoaoBispo - * */ public class SpecsGit { @@ -50,6 +51,13 @@ public class SpecsGit { private static final String URL_PARAM_BRANCH = "branch"; + /** + * Parses a repository URL and returns the folder where the repository is located. + * If the repository does not exist locally, it will be cloned. + * + * @param repositoryPath the URL of the repository + * @return the folder where the repository is located + */ public static File parseRepositoryUrl(String repositoryPath) { var repoFolder = parseRepositoryUrl(repositoryPath, true); @@ -75,14 +83,16 @@ public static File parseRepositoryUrl(String repositoryPath) { return repoFolder; } + /** + * Checks out the specified branch in the given Git repository. + * + * @param git the Git repository + * @param branchName the name of the branch to check out + */ public static void checkout(Git git, String branchName) { try { - // System.out.println("SETTING TO BRANCH " + branch); - var currentBranch = git.getRepository().getBranch(); - // System.out.println("CURRENT BRANCH: " + currentBranch); - // Checkout branch if (!branchName.equals(currentBranch)) { @@ -98,18 +108,6 @@ public static void checkout(Git git, String branchName) { git.checkout().setCreateBranch(createBranch) .setName(branchName) .call(); - - // var currentFullBranch = git.getRepository().getFullBranch(); - // var startPoint = currentFullBranch.substring(0, currentFullBranch.length() - - // currentBranch.length()) - // + branch; - // - // git.checkout().setCreateBranch(true) - // .setName(branch) - // .setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK) - // // .setStartPoint(startPoint) - // .call(); - } } catch (Exception e) { throw new RuntimeException( @@ -117,17 +115,14 @@ public static void checkout(Git git, String branchName) { } } - // public static String getBranchName(Git git) { - // try { - // var fullBranch = git.getRepository().getFullBranch(); - // var lastSlash = fullBranch.lastIndexOf('/'); - // return lastSlash != -1 ? fullBranch.substring(lastSlash + 1, fullBranch.length()) : fullBranch; - // } catch (IOException e) { - // throw new RuntimeException("Could not get the name of the branch of the git repository " + git, e); - // } - // - // } - + /** + * Parses a repository URL and returns the folder where the repository is located. + * If the repository does not exist locally, it will be cloned. + * + * @param repositoryPath the URL of the repository + * @param firstTime whether this is the first attempt to parse the repository URL + * @return the folder where the repository is located + */ private static File parseRepositoryUrl(String repositoryPath, boolean firstTime) { String repoName = getRepoName(repositoryPath); @@ -145,11 +140,6 @@ private static File parseRepositoryUrl(String repositoryPath, boolean firstTime) .setDirectory(repoFolder) .setCredentialsProvider(getCredentials(repositoryPath)) .call(); - // Check if there is a login/pass in the url - // CredentialsProvider cp = getCredentials(repositoryPath); - // System.out.println("SETTING NULL"); - // clone.setCredentialsProvider(null); - // clone.call(); return repoFolder; } catch (GitAPIException e) { @@ -158,7 +148,6 @@ private static File parseRepositoryUrl(String repositoryPath, boolean firstTime) } // Repository already exists, pull - try { SpecsLogs.msgInfo("Pulling repo '" + repositoryPath + "' in folder '" + repoFolder + "'"); Git gitRepo = Git.open(repoFolder); @@ -183,6 +172,12 @@ private static File parseRepositoryUrl(String repositoryPath, boolean firstTime) return repoFolder; } + /** + * Retrieves the credentials provider for the given repository URL. + * + * @param repositoryPath the URL of the repository + * @return the credentials provider, or null if no credentials are required + */ public static CredentialsProvider getCredentials(String repositoryPath) { var currentString = repositoryPath; @@ -201,10 +196,7 @@ public static CredentialsProvider getCredentials(String repositoryPath) { currentString = currentString.substring(0, firstSlashIndex); } - // System.out.println("CURRENT INDEX: " + currentString); - // Check if there is a login/pass in the url - // Look for last index, in case the login has an '@' var atIndex = currentString.lastIndexOf('@'); // No login @@ -213,7 +205,6 @@ public static CredentialsProvider getCredentials(String repositoryPath) { } var loginPass = currentString.substring(0, atIndex); - // System.out.println("LOGIN PASS: " + loginPass); // Split login pass var colonIndex = loginPass.indexOf(':'); @@ -237,15 +228,26 @@ public static CredentialsProvider getCredentials(String repositoryPath) { var login = loginPass.substring(0, colonIndex); var pass = loginPass.substring(colonIndex + 1, loginPass.length()); - // System.out.println("LOGIN: " + login); - // System.out.println("PASS: " + pass); return new UsernamePasswordCredentialsProvider(login, pass); } + /** + * Pulls the latest changes from the remote repository into the local repository. + * + * @param repoFolder the folder of the local repository + * @return the result of the pull operation + */ public static PullResult pull(File repoFolder) { return pull(repoFolder, null); } + /** + * Pulls the latest changes from the remote repository into the local repository. + * + * @param repoFolder the folder of the local repository + * @param cp the credentials provider + * @return the result of the pull operation + */ public static PullResult pull(File repoFolder, CredentialsProvider cp) { try { SpecsLogs.msgInfo("Pulling repo in folder '" + repoFolder + "'"); @@ -263,6 +265,12 @@ public static PullResult pull(File repoFolder, CredentialsProvider cp) { } } + /** + * Computes the differences between the working directory and the index of the repository. + * + * @param repoFolder the folder of the local repository + * @return a list of differences + */ public static List diff(File repoFolder) { try { SpecsLogs.msgInfo("Diff repo in folder '" + repoFolder + "'"); @@ -274,12 +282,28 @@ public static List diff(File repoFolder) { } } + /** + * Clones a repository into the specified folder. + * + * @param repositoryPath the URL of the repository + * @param outputFolder the folder where the repository will be cloned + * @param cp the credentials provider + * @return the Git object representing the cloned repository + */ public static Git clone(String repositoryPath, File outputFolder, CredentialsProvider cp) { return clone(repositoryPath, outputFolder, cp, null); } + /** + * Clones a repository into the specified folder and checks out the specified branch. + * + * @param repositoryPath the URL of the repository + * @param outputFolder the folder where the repository will be cloned + * @param cp the credentials provider + * @param branch the branch to check out + * @return the Git object representing the cloned repository + */ public static Git clone(String repositoryPath, File outputFolder, CredentialsProvider cp, String branch) { - // TODO: tag should be branch String repoName = getRepoName(repositoryPath); // Get repo folder @@ -311,15 +335,36 @@ public static Git clone(String repositoryPath, File outputFolder, CredentialsPro } } + /** + * Normalizes a tag name by adding the "refs/tags/" prefix if it is not already present. + * + * @param tag the tag name + * @return the normalized tag name + */ public static String normalizeTag(String tag) { var prefix = "refs/tags/"; return tag.startsWith(prefix) ? tag : prefix + tag; } + /** + * Checks if the specified tag exists in the remote repository. + * + * @param repositoryPath the URL of the repository + * @param tag the tag name + * @return true if the tag exists, false otherwise + */ public static boolean hasTag(String repositoryPath, String tag) { return hasTag(repositoryPath, tag, null); } + /** + * Checks if the specified tag exists in the remote repository. + * + * @param repositoryPath the URL of the repository + * @param tag the tag name + * @param credentialsProvider the credentials provider + * @return true if the tag exists, false otherwise + */ public static boolean hasTag(String repositoryPath, String tag, CredentialsProvider credentialsProvider) { LsRemoteCommand ls = Git.lsRemoteRepository(); try { @@ -334,17 +379,18 @@ public static boolean hasTag(String repositoryPath, String tag, CredentialsProvi .setTags(true) // include tags in result .callAsMap(); - // System.out.println("TAGS: " + remoteRefs.keySet()); - // - // var prefix = "refs/tags/"; - // var completeTag = tag.startsWith(prefix) ? tag : prefix + tag; - return remoteRefs.keySet().contains(normalizeTag(tag)); } catch (Exception e) { throw new RuntimeException("Could not check tags of repository '" + repositoryPath + "'", e); } } + /** + * Extracts the name of the repository from its URL. + * + * @param repositoryPath the URL of the repository + * @return the name of the repository + */ public static String getRepoName(String repositoryPath) { try { String repoPath = new URI(repositoryPath).getPath(); @@ -369,10 +415,24 @@ public static String getRepoName(String repositoryPath) { } + /** + * Returns the folder where repositories are stored. + * + * @return the folder where repositories are stored + */ public static File getRepositoriesFolder() { return new File(SpecsIo.getTempFolder(), SPECS_GIT_REPOS_FOLDER); } + /** + * Clones or pulls a repository into the specified folder. + * + * @param repositoryPath the URL of the repository + * @param outputFolder the folder where the repository will be cloned or pulled + * @param user the username for authentication + * @param password the password for authentication + * @return the folder where the repository is located + */ public static File cloneOrPull(String repositoryPath, File outputFolder, String user, String password) { String repoName = getRepoName(repositoryPath); @@ -384,25 +444,18 @@ public static File cloneOrPull(String repositoryPath, File outputFolder, String try { SpecsLogs.msgInfo("Cloning repo '" + repositoryPath + "' to folder '" + repoFolder + "'"); - // Delete folder to avoid errors - // if (SpecsIo.isEmptyFolder(repoFolder)) { - // SpecsIo.deleteFolder(repoFolder); - // } var git = Git.cloneRepository() .setURI(repositoryPath) .setDirectory(repoFolder); if (user != null & password != null) { CredentialsProvider cp = new UsernamePasswordCredentialsProvider(user, password); - git.setCredentialsProvider(cp);// .call(); + git.setCredentialsProvider(cp); } - // System.out.println("OUTPUT FOLDER:" + outputFolder); Git repo = git.call(); - // File workTree = git.getRepository().getWorkTree(); repo.close(); - // System.out.println("REPO: " + workTree); return repoFolder; } catch (GitAPIException e) { throw new RuntimeException("Could not clone repository '" + repositoryPath + "'", e); @@ -428,6 +481,13 @@ public static File cloneOrPull(String repositoryPath, File outputFolder, String return repoFolder; } + /** + * Adds the specified files to the index of the repository. + * + * @param repoFolder the folder of the local repository + * @param filesToAdd the files to add + * @return the result of the add operation + */ public static DirCache add(File repoFolder, List filesToAdd) { System.out.println("Git add to " + repoFolder); try { @@ -449,6 +509,12 @@ public static DirCache add(File repoFolder, List filesToAdd) { } } + /** + * Commits and pushes the changes in the repository. + * + * @param repoFolder the folder of the local repository + * @param cp the credentials provider + */ public static void commitAndPush(File repoFolder, CredentialsProvider cp) { System.out.println("Commiting and pushing " + repoFolder); @@ -461,21 +527,18 @@ public static void commitAndPush(File repoFolder, CredentialsProvider cp) { } + /** + * Checks if the specified commit value is the name of a branch in the repository. + * + * @param repo the Git repository + * @param commit the commit value + * @return true if the commit value is the name of a branch, false otherwise + */ public static boolean isBranchName(Git repo, String commit) { - // Check if commit value is the name of a branch - // Taken from here: https://stackoverflow.com/a/57365145 - try { - - // for (var branchRef : repo.branchList().setListMode(ListMode.REMOTE).call()) { - // System.out.println("BRANCH: " + branchRef.getName()); - // } - - // Pattern to search for var commitPattern = "refs/remotes/origin/" + commit; return repo.branchList() - // So that it returns all remotes .setListMode(ListMode.REMOTE) .call() .stream() diff --git a/GitPlus/test-experimental/pt/up/fe/specs/git/GitTester.java b/GitPlus/test-experimental/pt/up/fe/specs/git/GitTester.java index 3e0b9d71..3bdc725f 100644 --- a/GitPlus/test-experimental/pt/up/fe/specs/git/GitTester.java +++ b/GitPlus/test-experimental/pt/up/fe/specs/git/GitTester.java @@ -1,14 +1,14 @@ /** * Copyright 2024 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.git; @@ -19,14 +19,23 @@ import org.junit.Test; +/** + * Unit tests for the SpecsGit utility class. + */ public class GitTester { + /** + * Tests the presence of a specific tag in a remote Git repository. + */ @Test public void testTag() { assertTrue(SpecsGit.hasTag("https://github.com/specs-feup/clava.git", "clang_ast_dumper_v4.2.18")); assertTrue(!SpecsGit.hasTag("https://github.com/specs-feup/clava.git", "ddddd")); } + /** + * Tests cloning a remote Git repository to a local folder. + */ @Test public void testClone() { SpecsGit.clone("https://github.com/specs-feup/clava.git", new File("C:\\Temp\\clone_test"), null, diff --git a/Gprofer/src/pt/up/fe/specs/gprofer/Gprofer.java b/Gprofer/src/pt/up/fe/specs/gprofer/Gprofer.java index cec44cb3..2c820818 100644 --- a/Gprofer/src/pt/up/fe/specs/gprofer/Gprofer.java +++ b/Gprofer/src/pt/up/fe/specs/gprofer/Gprofer.java @@ -1,14 +1,14 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.gprofer; @@ -42,172 +42,182 @@ import pt.up.fe.specs.util.system.ProcessOutputAsString; import pt.up.fe.specs.util.utilities.LineStream; +/** + * Utility class for profiling binaries using gprof and parsing the results. + */ public class Gprofer { + /** + * Parses a gprof text profile from the given file path. + * + * @param textProfilePath path to the gprof text profile + * @return a GprofData object containing the parsed data + */ public static GprofData parseTextProfile(String textProfilePath) { - File textProfile = new File(textProfilePath); return parseTextProfile(textProfile); } + /** + * Parses a gprof text profile from the given file. + * + * @param textProfile file containing the gprof text profile + * @return a GprofData object containing the parsed data + */ private static GprofData parseTextProfile(File textProfile) { - return parseGprof(SpecsIo.toInputStream(textProfile)); } /** - * Runs on a temporary directory. - * - * @param binaryPath - * @return + * Profiles the given binary using gprof in a temporary directory. + * + * @param binaryPath path to the binary to profile + * @return a GprofData object containing the profiling results */ public static GprofData profile(String binaryPath) { - return profile(new File(binaryPath)); } /** - * Runs on a temporary directory. - * - * @param binary - * @return + * Profiles the given binary using gprof in a temporary directory. + * + * @param binary the binary file to profile + * @return a GprofData object containing the profiling results */ public static GprofData profile(File binary) { - return profile(binary, Collections.emptyList(), 1); } /** - * Runs on a temporary directory. - * - * @param binary - * @param args - * @param numRuns - * @return + * Profiles the given binary using gprof in a temporary directory, with arguments and number of runs. + * + * @param binary the binary file to profile + * @param args arguments to pass to the binary + * @param numRuns number of times to run the binary + * @return a GprofData object containing the profiling results */ public static GprofData profile(File binary, List args, int numRuns) { - File workingDir = SpecsIo.mkdir( SpecsIo.getTempFolder(), "gprofer_" + UUID.randomUUID().toString()); - boolean deleteWorkingDir = true; boolean checkReturn = true; - return profile(binary, args, numRuns, workingDir, deleteWorkingDir, checkReturn); } /** - * Runs on then provided directory. - * - * @param binary - * @param args - * @param numRuns - * @param workingDir - * @param deleteWorkingDir - * @param checkReturn - * TODO - * @return + * Profiles the given binary using gprof in the provided directory. + * + * @param binary the binary file to profile + * @param args arguments to pass to the binary + * @param numRuns number of times to run the binary + * @param workingDir the working directory to use + * @param deleteWorkingDir whether to delete the working directory after profiling + * @param checkReturn whether to check the return code of the process + * @return a GprofData object containing the profiling results */ public static GprofData profile(File binary, List args, int numRuns, File workingDir, boolean deleteWorkingDir, boolean checkReturn) { - if (!binary.exists()) { throw new RuntimeException("Could not locate the binary \"" + binary + "\"."); } - if (!workingDir.exists()) { throw new RuntimeException("Could not locate the working directory \"" + workingDir + "\"."); } - int currentRun = 0; - List gmons = new ArrayList<>(); - List filesToDelete = new ArrayList<>(); if (deleteWorkingDir) { filesToDelete.add(workingDir); } - while (currentRun < numRuns) { - runBinary(binary, args, workingDir, checkReturn); - makeGmon(currentRun, workingDir, filesToDelete, gmons); - currentRun++; } - GprofData data = summarizeGmons(binary, workingDir, gmons, filesToDelete); - deleteTempFiles(filesToDelete); - return data; } + /** + * Summarizes the gmon files into a single GprofData object. + * + * @param binary the binary file + * @param workingDir the working directory + * @param gmons the list of gmon files + * @param filesToDelete files to delete after processing + * @return a GprofData object containing the summarized data + */ private static GprofData summarizeGmons(File binary, File workingDir, List gmons, List filesToDelete) { - List command = new ArrayList<>(); command.add("gprof"); command.add("-bp"); command.add("-zc"); command.add("-s"); command.add(binary.getAbsolutePath()); - List gmonNames = gmons.stream().map(File::getAbsolutePath).collect(Collectors.toList()); command.addAll(gmonNames); - ProcessOutput result = SpecsSystem.runProcess( command, workingDir, Gprofer::parseGprof, Function.identity()); - File gmonSum = new File(workingDir, "gmon.sum"); filesToDelete.add(gmonSum); - return result.getStdOut(); } + /** + * Moves the gmon.out file to a new file for the current run. + * + * @param currentRun the current run index + * @param workingDir the working directory + * @param filesToDelete files to delete after processing + * @param gmons list to add the new gmon file to + */ private static void makeGmon(int currentRun, File workingDir, List filesToDelete, List gmons) { - File gmon = new File(workingDir, "gmon.out"); File newGmon = new File(gmon.getAbsolutePath() + "." + currentRun); gmons.add(newGmon); - try { Files.move(gmon.toPath(), newGmon.toPath(), StandardCopyOption.ATOMIC_MOVE); } catch (Exception e) { throw new RuntimeException("Could not move file '" + gmon + "'", e); } - filesToDelete.add(newGmon); } + /** + * Runs the binary with the given arguments in the specified working directory. + * + * @param binary the binary file + * @param args arguments to pass to the binary + * @param workingDir the working directory + * @param checkReturn whether to check the return code of the process + */ private static void runBinary(File binary, List args, File workingDir, boolean checkReturn) { - List binaryCommand = new ArrayList<>(); binaryCommand.add(binary.getAbsolutePath()); binaryCommand.addAll(args); - ProcessOutputAsString result = SpecsSystem.runProcess(binaryCommand, workingDir, true, false); - if (checkReturn && result.isError()) { - SpecsLogs.setPrintStackTrace(false); SpecsLogs.warn("Could not profile the binary \"" + binary + "\". Execution terminated with error."); SpecsLogs.warn("stdout: " + result.getStdOut()); SpecsLogs.warn("stderr: " + result.getStdErr()); SpecsLogs.setPrintStackTrace(true); - throw new RuntimeException(); } } + /** + * Deletes the temporary files and folders used during profiling. + * + * @param filesToDelete list of files and folders to delete + */ private static void deleteTempFiles(List filesToDelete) { - for (File file : filesToDelete) { - if (file.isDirectory()) { SpecsIo.deleteFolder(file); } else { @@ -216,40 +226,61 @@ private static void deleteTempFiles(List filesToDelete) { } } + /** + * Converts the given GprofData object to its JSON representation. + * + * @param data the GprofData object + * @return a JSON string representing the data + */ public static String getJsonData(GprofData data) { - return new Gson().toJson(data); } + /** + * Parses gprof output from a string. + * + * @param gprofOutput the gprof output as a string + * @return a GprofData object containing the parsed data + */ private static GprofData parseGprof(String gprofOutput) { - InputStream gprofStream = new ByteArrayInputStream(gprofOutput.getBytes(Charset.defaultCharset())); - return parseGprof(gprofStream); } + /** + * Parses gprof output from an InputStream. + * + * @param gprofStream the gprof output as an InputStream + * @return a GprofData object containing the parsed data + */ private static GprofData parseGprof(InputStream gprofStream) { - Map table = parseTable(gprofStream); List hotspots = makeHotspots(table); - return new GprofData(table, hotspots); } + /** + * Creates a list of hotspots sorted by percentage from the profiling table. + * + * @param table the profiling table + * @return a list of function names sorted by percentage + */ private static List makeHotspots(Map table) { - List hotspots = table.values().stream() .sorted(Comparator.comparing(GprofLine::getPercentage).reversed()) .map(row -> row.getName()) .collect(Collectors.toList()); - return hotspots; } + /** + * Parses the profiling table from the gprof output InputStream. + * + * @param gprofOutput the gprof output InputStream + * @return a map of function names to GprofLine objects + */ private static Map parseTable(InputStream gprofOutput) { - LineStream lines = LineStream.newInstance(gprofOutput, "gprof output"); - return lines.stream() .map(Gprofer::parseLine) .filter(Optional::isPresent) @@ -257,36 +288,32 @@ private static Map parseTable(InputStream gprofOutput) { .collect(Collectors.toMap(GprofLine::getName, Function.identity())); } + /** + * Parses a single line of gprof output into a GprofLine object. + * + * @param line the line of gprof output + * @return an Optional containing the GprofLine if parsing was successful, or empty otherwise + */ private static Optional parseLine(String line) { - StringSplitter splitter = new StringSplitter(line.trim()); - Optional percentageTry = splitter.parseTry(StringSplitterRules::doubleNumber); if (percentageTry.isPresent()) { - Double percentage = percentageTry.get(); Double cumulativeSeconds = splitter.parse(StringSplitterRules::doubleNumber); Double selfSeconds = splitter.parse(StringSplitterRules::doubleNumber); - Integer calls = null; Double selfMsCall = null; Double totalMsCall = null; - Optional callsTry = splitter.parseTry(StringSplitterRules::integer); if (callsTry.isPresent()) { - calls = callsTry.get(); selfMsCall = splitter.parse(StringSplitterRules::doubleNumber); totalMsCall = splitter.parse(StringSplitterRules::doubleNumber); } - - // String name = splitter.parse(StringSplitterRules::string); String name = splitter.toString(); name = name.replaceAll(", ", ","); - GprofLine gproferRow = new GprofLine(percentage, cumulativeSeconds, selfSeconds, calls, selfMsCall, totalMsCall, name); - return Optional.ofNullable(gproferRow); } return Optional.empty(); diff --git a/Gprofer/src/pt/up/fe/specs/gprofer/GproferMain.java b/Gprofer/src/pt/up/fe/specs/gprofer/GproferMain.java index 13446c2c..a0ed29b9 100644 --- a/Gprofer/src/pt/up/fe/specs/gprofer/GproferMain.java +++ b/Gprofer/src/pt/up/fe/specs/gprofer/GproferMain.java @@ -1,14 +1,14 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.gprofer; @@ -19,40 +19,47 @@ import pt.up.fe.specs.gprofer.data.GprofData; +/** + * Entry point for running gprofer profiling from the command line. + */ public class GproferMain { + /** + * Main method for running a profiling session and printing the results as JSON. + * + * @param args command line arguments (not used in this example) + */ public static void main(String[] args) { - File binary = new File( "/home/pedro/Documents/repositories/AntarexIT4I-master/Betweenness/Code/build/betweenness"); List binaryArgs = Arrays.asList("-f", "/home/pedro/Documents/repositories/AntarexIT4I-master/Betweenness/Graphs/graph-prt-port.csv"); - int numRuns = 1; - - // File workingDir = new File("/home/pedro/Desktop/gprof_tests/src/"); - // boolean deleteWorkingDir = false; - // boolean checkReturn = true; - GprofData data = Gprofer.profile(binary, binaryArgs, numRuns); - System.out.println(Gprofer.getJsonData(data)); - - // warn("This is a warning message"); } + /** + * Prints a warning message with stack trace information. + * + * @param message the warning message + */ static void warn(String message) { - StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); StackTraceElement s = stackTrace[2]; // 0 is getStracktrace, 1 is this method - System.out.printf(buildShortMessage(message, s)); } + /** + * Builds a short message with class, method, file, and line number information. + * + * @param message the message + * @param s the stack trace element + * @return the formatted message + */ static String buildShortMessage(String message, StackTraceElement s) { - StringBuilder builder = new StringBuilder(message); builder.append(" "); builder.append(s.getClassName()); @@ -63,7 +70,6 @@ static String buildShortMessage(String message, StackTraceElement s) { builder.append(":"); builder.append(s.getLineNumber()); builder.append(")"); - return builder.toString(); } diff --git a/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofData.java b/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofData.java index 2d63b3f5..ddacdd4d 100644 --- a/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofData.java +++ b/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofData.java @@ -1,14 +1,14 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.gprofer.data; @@ -16,20 +16,39 @@ import java.util.List; import java.util.Map; +/** + * Represents the parsed data from a gprof profiling session, including the table of profiling lines and the list of hotspots. + */ public class GprofData { private final Map table; private final List hotspots; + /** + * Constructs a GprofData object with the given table and hotspots. + * + * @param table a map of function names to their profiling data + * @param hotspots a list of function names sorted by their profiling percentage + */ public GprofData(Map table, List hotspots) { this.table = table; this.hotspots = hotspots; } + /** + * Returns the table of profiling data. + * + * @return a map of function names to GprofLine objects + */ public Map getTable() { return table; } + /** + * Returns the list of hotspots (function names sorted by percentage). + * + * @return a list of function names + */ public List getHotspots() { return hotspots; } diff --git a/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofLine.java b/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofLine.java index 86d1b35f..e2331add 100644 --- a/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofLine.java +++ b/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofLine.java @@ -1,18 +1,21 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.gprofer.data; +/** + * Represents a single line of profiling data from gprof output. + */ public class GprofLine { private final Double percentage; @@ -23,6 +26,17 @@ public class GprofLine { private final Double totalMsCall; private final String name; + /** + * Constructs a GprofLine with the given profiling values. + * + * @param percentage the percentage of time spent in this function + * @param cumulativeSeconds the cumulative seconds up to this function + * @param selfSeconds the self seconds spent in this function + * @param calls the number of calls to this function + * @param selfMsCall the self milliseconds per call + * @param totalMsCall the total milliseconds per call + * @param name the function name + */ public GprofLine(Double percentage, Double cumulativeSeconds, Double selfSeconds, Integer calls, Double selfMsCall, Double totalMsCall, String name) { this.percentage = percentage; @@ -34,30 +48,51 @@ public GprofLine(Double percentage, Double cumulativeSeconds, Double selfSeconds this.name = name; } + /** + * @return the percentage of time spent in this function + */ public Double getPercentage() { return percentage; } + /** + * @return the cumulative seconds up to this function + */ public Double getCumulativeSeconds() { return cumulativeSeconds; } + /** + * @return the self seconds spent in this function + */ public Double getSelfSeconds() { return selfSeconds; } + /** + * @return the number of calls to this function + */ public Integer getCalls() { return calls; } + /** + * @return the self milliseconds per call + */ public Double getSelfMsCall() { return selfMsCall; } + /** + * @return the total milliseconds per call + */ public Double getTotalMsCall() { return totalMsCall; } + /** + * @return the function name + */ public String getName() { return name; } diff --git a/GsonPlus/src/org/suikasoft/GsonPlus/JsonPersistence.java b/GsonPlus/src/org/suikasoft/GsonPlus/JsonPersistence.java index 77cadac5..78077a02 100644 --- a/GsonPlus/src/org/suikasoft/GsonPlus/JsonPersistence.java +++ b/GsonPlus/src/org/suikasoft/GsonPlus/JsonPersistence.java @@ -1,14 +1,14 @@ /** * Copyright 2012 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.GsonPlus; @@ -18,38 +18,47 @@ import pt.up.fe.specs.util.utilities.PersistenceFormat; /** - * @author Joao Bispo - * + * Implementation of {@link PersistenceFormat} for JSON serialization using Gson. */ public class JsonPersistence extends PersistenceFormat { private final Gson gson; /** - * + * Constructs a new JsonPersistence instance with a default Gson object. */ public JsonPersistence() { gson = new Gson(); } - /* (non-Javadoc) - * @see org.specs.DymaLib.Graphs.Utils.PersistenceFormat.PersistenceFormat#to(java.lang.Object, java.lang.Object[]) + /** + * Serializes the given object to a JSON string. + * + * @param anObject the object to serialize + * @return the JSON string */ @Override public String to(Object anObject) { return gson.toJson(anObject); } - /* (non-Javadoc) - * @see org.specs.DymaLib.Graphs.Utils.PersistenceFormat.PersistenceFormat#from(java.lang.String, java.lang.Class, java.lang.Object[]) + /** + * Deserializes the given JSON string to an object of the specified class. + * + * @param contents the JSON string + * @param classOfObject the class to deserialize to + * @param the type of the object + * @return the deserialized object */ @Override public T from(String contents, Class classOfObject) { return gson.fromJson(contents, classOfObject); } - /* (non-Javadoc) - * @see pt.up.fe.specs.util.Utilities.PersistenceFormat#getExtension() + /** + * Returns the file extension for JSON files. + * + * @return the string "json" */ @Override public String getExtension() { diff --git a/GsonPlus/src/org/suikasoft/GsonPlus/SpecsGson.java b/GsonPlus/src/org/suikasoft/GsonPlus/SpecsGson.java index e53b1b5c..b1b4ccd9 100644 --- a/GsonPlus/src/org/suikasoft/GsonPlus/SpecsGson.java +++ b/GsonPlus/src/org/suikasoft/GsonPlus/SpecsGson.java @@ -1,14 +1,14 @@ /** * Copyright 2019 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.GsonPlus; @@ -23,42 +23,76 @@ import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; +/** + * Utility class for working with Gson serialization and deserialization. + */ public class SpecsGson { + /** + * Serializes an object to a pretty-printed JSON string. + * + * @param object the object to serialize + * @return the JSON string + */ public static String toJson(Object object) { return new GsonBuilder().setPrettyPrinting().create().toJson(object); } + /** + * Deserializes a JSON string to an object of the given class. + * + * @param json the JSON string + * @param aClass the class to deserialize to + * @param the type of the object + * @return the deserialized object + */ public static T fromJson(String json, Class aClass) { return new Gson().fromJson(json, aClass); } + /** + * Deserializes a JSON string to a map. + * + * @param json the JSON string + * @return the deserialized map + */ @SuppressWarnings("unchecked") public static Map fromJson(String json) { return new Gson().fromJson(json, Map.class); } + /** + * Converts a JsonElement array to a list using the given mapper function. + * + * @param element the JsonElement (must be an array) + * @param mapper function to map each JsonElement to the desired type + * @param the type of the list elements + * @return the list of mapped elements + */ public static List asList(JsonElement element, Function mapper) { if (!element.isJsonArray()) { throw new RuntimeException("Can only be applied to arrays: " + element); } - var array = element.getAsJsonArray(); - List list = new ArrayList<>(array.size()); - for (var arrayElemen : array) { list.add(mapper.apply(arrayElemen)); } - return list; } + /** + * Converts a JsonElement to an Optional using the given mapper function. + * + * @param element the JsonElement + * @param mapper function to map the JsonElement to the desired type + * @param the type of the optional value + * @return an Optional containing the mapped value, or empty if the element is null + */ public static Optional asOptional(JsonElement element, Function mapper) { if (element == null) { return Optional.empty(); } - return Optional.of(mapper.apply(element)); } } diff --git a/JacksonPlus/src/pt/up/fe/specs/JacksonPlus/SpecsJackson.java b/JacksonPlus/src/pt/up/fe/specs/JacksonPlus/SpecsJackson.java index 1ea4a27d..cf6301ce 100644 --- a/JacksonPlus/src/pt/up/fe/specs/JacksonPlus/SpecsJackson.java +++ b/JacksonPlus/src/pt/up/fe/specs/JacksonPlus/SpecsJackson.java @@ -1,14 +1,14 @@ /** * Copyright 2020 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.JacksonPlus; @@ -25,38 +25,62 @@ import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; /** - * Wrapper class with utility methods to use Jackson. - * - * @author pedro - * + * Wrapper class with utility methods to use Jackson for JSON serialization and deserialization. */ public class SpecsJackson { + /** + * Reads an object from a JSON file at the given path. + * + * @param filePath the path to the JSON file + * @param clazz the class to deserialize to + * @param the type of the object + * @return the deserialized object + */ public static T fromFile(String filePath, Class clazz) { - return fromFile(filePath, clazz, false); } + /** + * Reads an object from a JSON file at the given path, with optional type info. + * + * @param filePath the path to the JSON file + * @param clazz the class to deserialize to + * @param hasTypeInfo whether to use type information + * @param the type of the object + * @return the deserialized object + */ public static T fromFile(String filePath, Class clazz, boolean hasTypeInfo) { - File file = new File(filePath); - return fromFile(file, clazz, hasTypeInfo); } + /** + * Reads an object from a JSON file. + * + * @param file the JSON file + * @param clazz the class to deserialize to + * @param the type of the object + * @return the deserialized object + */ public static T fromFile(File file, Class clazz) { - return fromFile(file, clazz, false); } + /** + * Reads an object from a JSON file, with optional type info. + * + * @param file the JSON file + * @param clazz the class to deserialize to + * @param hasTypeInfo whether to use type information + * @param the type of the object + * @return the deserialized object + */ public static T fromFile(File file, Class clazz, boolean hasTypeInfo) { - try { FileReader fr = new FileReader(file); BufferedReader br = new BufferedReader(fr); - ObjectMapper mapper = new ObjectMapper(); - if (hasTypeInfo) { PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder() .allowIfBaseType(Object.class) @@ -70,23 +94,36 @@ public static T fromFile(File file, Class clazz, boolean hasTypeInfo) { } } + /** + * Reads an object from a JSON string. + * + * @param string the JSON string + * @param clazz the class to deserialize to + * @param the type of the object + * @return the deserialized object + */ public static T fromString(String string, Class clazz) { - return fromString(string, clazz, false); } + /** + * Reads an object from a JSON string, with optional type info. + * + * @param string the JSON string + * @param clazz the class to deserialize to + * @param hasTypeInfo whether to use type information + * @param the type of the object + * @return the deserialized object + */ public static T fromString(String string, Class clazz, boolean hasTypeInfo) { - try { ObjectMapper mapper = new ObjectMapper(); - if (hasTypeInfo) { PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder() .allowIfBaseType(Object.class) .build(); mapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL); } - T object = mapper.readValue(string, clazz); return object; } catch (JsonProcessingException e) { @@ -94,49 +131,70 @@ public static T fromString(String string, Class clazz, boolean hasTypeInf } } + /** + * Writes an object to a JSON file. + * + * @param object the object to serialize + * @param file the file to write to + * @param the type of the object + */ public static void toFile(T object, File file) { - toFile(object, file, false); } + /** + * Writes an object to a JSON file, with optional type info. + * + * @param object the object to serialize + * @param file the file to write to + * @param embedTypeInfo whether to embed type information + * @param the type of the object + */ public static void toFile(T object, File file, boolean embedTypeInfo) { - try { FileWriter fw = new FileWriter(file); BufferedWriter bw = new BufferedWriter(fw); - ObjectMapper mapper = new ObjectMapper(); - if (embedTypeInfo) { PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder() .allowIfBaseType(Object.class) .build(); mapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL); } - mapper.writeValue(bw, object); } catch (Exception e) { throw new RuntimeException(e); } } + /** + * Serializes an object to a JSON string. + * + * @param object the object to serialize + * @param the type of the object + * @return the JSON string + */ public static String toString(T object) { - return toString(object, false); } + /** + * Serializes an object to a JSON string, with optional type info. + * + * @param object the object to serialize + * @param embedTypeInfo whether to embed type information + * @param the type of the object + * @return the JSON string + */ public static String toString(T object, boolean embedTypeInfo) { - try { ObjectMapper mapper = new ObjectMapper(); - if (embedTypeInfo) { PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder() .allowIfBaseType(Object.class) .build(); mapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL); } - return mapper.writeValueAsString(object); } catch (JsonProcessingException e) { throw new RuntimeException(e); diff --git a/JadxPlus/src/pt/up/fe/specs/jadx/DecompilationFailedException.java b/JadxPlus/src/pt/up/fe/specs/jadx/DecompilationFailedException.java index 95c01678..538de449 100644 --- a/JadxPlus/src/pt/up/fe/specs/jadx/DecompilationFailedException.java +++ b/JadxPlus/src/pt/up/fe/specs/jadx/DecompilationFailedException.java @@ -1,22 +1,31 @@ /** * Copyright 2022 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.jadx; +/** + * Exception thrown when decompilation with Jadx fails. + */ public class DecompilationFailedException extends Exception { private static final long serialVersionUID = 5280339014409302798L; + /** + * Constructs a new DecompilationFailedException with the specified message and cause. + * + * @param msg the detail message + * @param err the cause of the exception + */ public DecompilationFailedException(String msg, Throwable err) { super(msg, err); } diff --git a/JadxPlus/src/pt/up/fe/specs/jadx/SpecsJadx.java b/JadxPlus/src/pt/up/fe/specs/jadx/SpecsJadx.java index 941492b5..8101cf04 100644 --- a/JadxPlus/src/pt/up/fe/specs/jadx/SpecsJadx.java +++ b/JadxPlus/src/pt/up/fe/specs/jadx/SpecsJadx.java @@ -1,14 +1,14 @@ /** * Copyright 2022 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.jadx; @@ -31,6 +31,9 @@ import pt.up.fe.specs.util.SpecsLogs; import pt.up.fe.specs.util.SpecsXml; +/** + * Utility class for decompiling APK files using Jadx and managing decompilation cache. + */ public class SpecsJadx { private static final Map CACHED_DECOMPILATIONS = new HashMap<>(); @@ -41,19 +44,38 @@ public class SpecsJadx { static { var baseCacheFolder = getCacheFolder(); - SpecsIo.deleteFolderContents(baseCacheFolder); baseCacheFolder.deleteOnExit(); } + /** + * Returns the folder used for caching decompilations. + * + * @return the cache folder + */ public static File getCacheFolder() { return SpecsIo.getTempFolder(CACHE_FOLDERNAME); } + /** + * Decompiles the given APK file and returns the output folder. + * + * @param apk the APK file to decompile + * @return the folder containing the decompiled files + * @throws DecompilationFailedException if decompilation fails + */ public File decompileAPK(File apk) throws DecompilationFailedException { return decompileAPK(apk, null); } + /** + * Decompiles the given APK file with an optional package filter and returns the output folder. + * + * @param apk the APK file to decompile + * @param packageFilter a list of package patterns to filter classes (can be null) + * @return the folder containing the decompiled files + * @throws DecompilationFailedException if decompilation fails + */ public File decompileAPK(File apk, List packageFilter) throws DecompilationFailedException { // Delete cache if filter changed @@ -145,6 +167,12 @@ public void progress(long done, long total) { } } + /** + * Strips the given pattern into components for filtering. + * + * @param pattern the pattern to strip + * @return an array containing the stripped components + */ private String[] stripPattern(String pattern) { String[] filter = new String[2]; @@ -169,6 +197,13 @@ private String[] stripPattern(String pattern) { return filter; } + /** + * Builds a filter predicate based on the given pattern and package name. + * + * @param pattern the filter pattern + * @param packageName the package name to filter + * @return a predicate for filtering class names + */ private Predicate buildFilter(String pattern, String packageName) { if (pattern.isEmpty()) diff --git a/JavaGenerator/src/org/specs/generators/java/IGenerate.java b/JavaGenerator/src/org/specs/generators/java/IGenerate.java index 57311e26..8326ce9e 100644 --- a/JavaGenerator/src/org/specs/generators/java/IGenerate.java +++ b/JavaGenerator/src/org/specs/generators/java/IGenerate.java @@ -8,24 +8,33 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. - */ -/** - * + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java; import org.specs.generators.java.utils.Utils; /** - * @author Tiago + * Interface for code generation in JavaGenerator. Implementing classes should provide a method to generate Java code + * with a specified indentation level. * + * @author Tiago */ public interface IGenerate { - public StringBuilder generateCode(int indentation); + /** + * Generates code for the implementing object with the given indentation. + * + * @param indentation the indentation level + * @return a StringBuilder containing the generated code + */ + StringBuilder generateCode(int indentation); + /** + * Returns the platform-specific line separator. + * + * @return the line separator string + */ default String ln() { return Utils.ln(); - //return SpecsIo.getNewline(); } } diff --git a/JavaGenerator/src/org/specs/generators/java/classtypes/ClassType.java b/JavaGenerator/src/org/specs/generators/java/classtypes/ClassType.java index 415b50b7..bccb2c31 100644 --- a/JavaGenerator/src/org/specs/generators/java/classtypes/ClassType.java +++ b/JavaGenerator/src/org/specs/generators/java/classtypes/ClassType.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - *

+ * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - *

+ * * http://www.apache.org/licenses/LICENSE-2.0 - *

+ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.classtypes; @@ -28,6 +28,10 @@ import java.util.List; import java.util.Optional; +/** + * Abstract base class for Java class, interface, and enum representations in JavaGenerator. + * Provides common fields and methods for code generation, including package, imports, JavaDoc, privacy, modifiers, and inner types. + */ public abstract class ClassType implements IGenerate { // Class package and imports @@ -48,9 +52,9 @@ public abstract class ClassType implements IGenerate { private Optional parent; /** - * Create a public class type with name and package + * Creates a public class type with name and package. * - * @param name the name for the class + * @param name the name for the class * @param classPackage the class package */ public ClassType(String name, String classPackage) { @@ -69,6 +73,11 @@ private void init(String name, String classPackage) { setInnerTypes(new UniqueList<>()); } + /** + * Returns the fully qualified name of the class. + * + * @return the qualified name + */ public String getQualifiedName() { String thePackage = classPackage != null && !classPackage.isEmpty() ? classPackage + "." : ""; @@ -76,19 +85,19 @@ public String getQualifiedName() { } /** - * Return a list of all imports, including sub imports + * Returns the package of the class. * - * @return + * @return the class package */ - // List getAllImports(); - // - // Optional getParent(); - // - // void setParent(ClassType type); public String getClassPackage() { return classPackage; } + /** + * Sets the package of the class. + * + * @param classPackage the class package + */ public void setClassPackage(String classPackage) { this.classPackage = classPackage; } @@ -154,8 +163,8 @@ public void setParent(ClassType classType) { *

* Note: this method sets the parent of the type given as the invoked one * - * @param type - * @return + * @param type the inner class type to add + * @return true if the inner class type was successfully added */ public boolean add(ClassType type) { @@ -167,6 +176,11 @@ public boolean add(ClassType type) { } + /** + * Returns a list of all imports, including sub imports. + * + * @return the list of all imports + */ public List getAllImports() { List imports = new UniqueList<>(); @@ -178,10 +192,9 @@ public List getAllImports() { } /** - * Add an import to the class + * Adds one or more imports to the class. * - * @param imports the new import - * @return true if the import can be added, false if not + * @param imports the new imports */ public void addImport(String... imports) { for (final String newImport : imports) { @@ -189,6 +202,12 @@ public void addImport(String... imports) { } } + /** + * Adds an import to the class. + * + * @param newImport the new import + * @return true if the import can be added, false if not + */ public boolean addImport(String newImport) { if (getClassPackage().equals(StringUtils.getPackage(newImport))) { @@ -199,10 +218,9 @@ public boolean addImport(String newImport) { } /** - * Add an import to the class + * Adds one or more imports to the class. * - * @param imports the new import - * @return true if the import can be added, false if not + * @param imports the new imports */ public void addImport(Class... imports) { for (final Class newImport : imports) { @@ -211,10 +229,9 @@ public void addImport(Class... imports) { } /** - * Add an array of imports to the class + * Adds an array of imports to the class. * - * @param imports the new import - * @return true if the import can be added, false if not + * @param imports the new imports */ public void addImport(JavaType... imports) { for (final JavaType newImport : imports) { @@ -223,7 +240,7 @@ public void addImport(JavaType... imports) { } /** - * Add an import to the interface + * Adds an import to the class. * * @param newImport the new import * @return true if the import can be added, false if not @@ -240,9 +257,9 @@ public boolean addImport(JavaType newImport) { } /** - * Add the imports required for each generic type used in a JavaGenericType + * Adds the imports required for each generic type used in a JavaGenericType. * - * @param genType + * @param genType the generic type */ private void addGenericImports(JavaGenericType genType) { addImport(genType.getTheType()); @@ -250,7 +267,7 @@ private void addGenericImports(JavaGenericType genType) { } /** - * Remove an import from the class + * Removes an import from the class. * * @param importRem the import to be removed * @return true if the import was successfully removed @@ -260,7 +277,7 @@ public boolean removeImport(String importRem) { } /** - * Append text to the javadoc comment + * Appends text to the javadoc comment. * * @param comment the text to append * @return the {@link StringBuilder} with the new comment @@ -270,7 +287,7 @@ public StringBuilder appendComment(String comment) { } /** - * Add a new javadoc tag to the comment, with no description + * Adds a new javadoc tag to the comment, with no description. * * @param tag the new tag to add */ @@ -279,9 +296,9 @@ public void add(JDocTag tag) { } /** - * Add a new javadoc tag to the comment with description + * Adds a new javadoc tag to the comment with description. * - * @param tag the new tag to add + * @param tag the new tag to add * @param description the tag description */ public void add(JDocTag tag, String description) { @@ -289,7 +306,7 @@ public void add(JDocTag tag, String description) { } /** - * Add a new modifier to the class + * Adds a new modifier to the class. * * @param modifier the new modifier * @return true if the modifier was successfully added @@ -299,7 +316,7 @@ public boolean add(Modifier modifier) { } /** - * Removes a modifier from the class + * Removes a modifier from the class. * * @param modifier the modifier to remove * @return true if the modifier was successfully removed @@ -309,7 +326,7 @@ public boolean remove(Modifier modifier) { } /** - * Add a new annotation to the class + * Adds a new annotation to the class. * * @param annotation the new annotation * @return true if the annotation was successfully added @@ -319,7 +336,7 @@ public boolean add(Annotation annotation) { } /** - * Removes a annotation from the class + * Removes an annotation from the class. * * @param annotation the annotation to remove * @return true if the annotation was successfully removed @@ -328,6 +345,12 @@ public boolean remove(Annotation annotation) { return getAnnotations().remove(annotation); } + /** + * Generates the header of the class. + * + * @param indentation the indentation level + * @return the generated class header + */ public StringBuilder generateClassHeader(int indentation) { StringBuilder classGen = new StringBuilder(); if (!getParent().isPresent()) { // I'm the main class @@ -366,6 +389,12 @@ public StringBuilder generateClassHeader(int indentation) { return classGen; } + /** + * Generates the tail of the class. + * + * @param indentation the indentation level + * @return the generated class tail + */ public StringBuilder generateClassTail(int indentation) { StringBuilder classGen = new StringBuilder(); if (!getInnerTypes().isEmpty()) { diff --git a/JavaGenerator/src/org/specs/generators/java/classtypes/Interface.java b/JavaGenerator/src/org/specs/generators/java/classtypes/Interface.java index c7ddcb3f..5ea4ce45 100644 --- a/JavaGenerator/src/org/specs/generators/java/classtypes/Interface.java +++ b/JavaGenerator/src/org/specs/generators/java/classtypes/Interface.java @@ -1,14 +1,14 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.classtypes; @@ -23,8 +23,8 @@ import tdrc.utils.StringUtils; /** - * Java interface generation - * + * Represents a Java interface for code generation. Provides methods to manage fields, methods, and extended interfaces. + * * @author Tiago @ */ public class Interface extends ClassType { @@ -35,12 +35,10 @@ public class Interface extends ClassType { private List methods; /** - * Create a public interface with name and package - * - * @param name - * the name for the interface - * @param interfacePackage - * the interface package + * Create a public interface with name and package. + * + * @param name the name for the interface + * @param interfacePackage the interface package */ public Interface(String name, String interfacePackage) { super(name, interfacePackage); @@ -48,7 +46,7 @@ public Interface(String name, String interfacePackage) { } /** - * Initialize the Interface' lists + * Initialize the Interface' lists. */ private void init() { interfaces = new UniqueList<>(); @@ -58,9 +56,8 @@ private void init() { /** * Generate the corresponding java interface code, containing the package, imports, fields, methods, etc. - * - * @param indentation - * level of indentation + * + * @param indentation level of indentation * @return the generated java interface code */ @Override @@ -98,16 +95,14 @@ public StringBuilder generateCode(int indentation) { /** * Add a new extended interface to the interface. This method automatically adds the required import for the added - * interface - * - * @param interfaceinterface - * the new interface + * interface. + * + * @param interfaceinterface the new interface * @return true if the interface was successfully added */ public boolean addInterface(JavaType interfaceinterface) { final boolean isAdded = interfaces.add(interfaceinterface); if (isAdded) { - addImport(interfaceinterface); } return isAdded; @@ -115,10 +110,9 @@ public boolean addInterface(JavaType interfaceinterface) { /** * Removes an interface from the interface. This does not remove automatically the required import related to the - * removed interface - * - * @param interfaceinterface - * the interface to remove + * removed interface. + * + * @param interfaceinterface the interface to remove * @return true if the interface was successfully removed */ public boolean removeInterface(String interfaceinterface) { @@ -126,10 +120,9 @@ public boolean removeInterface(String interfaceinterface) { } /** - * Add a new field to the interface - * - * @param field - * the new field + * Add a new field to the interface. + * + * @param field the new field * @return true if the field was successfully added */ public boolean addField(Field field) { @@ -137,16 +130,14 @@ public boolean addField(Field field) { if (ret) { field.setDefaultInitializer(true); addImport(field.getType()); - } return ret; } /** - * Removes a field from the interface - * - * @param field - * the field to remove + * Removes a field from the interface. + * + * @param field the field to remove * @return true if the field was successfully removed */ public boolean removeField(Field field) { @@ -154,12 +145,11 @@ public boolean removeField(Field field) { } /** - * Add a new method to the interface. THis method automatically adds the imports required for the return type and + * Add a new method to the interface. This method automatically adds the imports required for the return type and * the arguments. Note that if the method is updated (e.g.: change return type or add arguments) the imports are not * updated. - * - * @param method - * the new method + * + * @param method the new method * @return true if the method was successfully added */ public boolean addMethod(Method method) { @@ -175,10 +165,9 @@ public boolean addMethod(Method method) { } /** - * Removes a method from the interface - * - * @param method - * the method to remove + * Removes a method from the interface. + * + * @param method the method to remove * @return true if the method was successfully removed */ public boolean removeMethod(Method method) { diff --git a/JavaGenerator/src/org/specs/generators/java/classtypes/JavaClass.java b/JavaGenerator/src/org/specs/generators/java/classtypes/JavaClass.java index 955e5ca6..bdacced9 100644 --- a/JavaGenerator/src/org/specs/generators/java/classtypes/JavaClass.java +++ b/JavaGenerator/src/org/specs/generators/java/classtypes/JavaClass.java @@ -8,7 +8,7 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.classtypes; @@ -26,7 +26,7 @@ import java.util.List; /** - * Java class generation + * Represents a Java class for code generation. Provides methods to manage fields, methods, constructors, and interfaces. * * @author Tiago */ @@ -41,19 +41,18 @@ public class JavaClass extends ClassType { private List constructors; /** - * Create a public class based on a {@link JavaType} + * Creates a public class based on a {@link JavaType}. * - * @param name the name for the class - * @param classPackage the class package + * @param javaType the JavaType for the class */ public JavaClass(JavaType javaType) { this(javaType.getName(), javaType.getPackage()); } /** - * Create a public class with name and package + * Creates a public class with name and package. * - * @param name the name for the class + * @param name the name for the class * @param classPackage the class package */ public JavaClass(String name, String classPackage) { @@ -61,10 +60,11 @@ public JavaClass(String name, String classPackage) { } /** - * Create a public class with name and package + * Creates a public class with name, package, and modifier. * - * @param name the name for the class + * @param name the name for the class * @param classPackage the class package + * @param modifier the class modifier */ public JavaClass(String name, String classPackage, Modifier modifier) { super(name, classPackage); @@ -72,9 +72,9 @@ public JavaClass(String name, String classPackage, Modifier modifier) { } /** - * Initialize the JavaClass' lists + * Initializes the JavaClass' lists. * - * @param modifier + * @param modifier the class modifier */ private void init(Modifier modifier) { @@ -89,10 +89,10 @@ private void init(Modifier modifier) { } /** - * Generate the corresponding java class code, containing the package, imports, fields methods, etc. + * Generates the corresponding Java class code, containing the package, imports, fields, methods, etc. * - * @param indentaton level of indentation - * @return the generated java class code + * @param indentation the indentation level + * @return the generated Java class code */ @Override public StringBuilder generateCode(int indentation) { @@ -118,6 +118,12 @@ public StringBuilder generateCode(int indentation) { return classGen; } + /** + * Adds methods to the class code generation. + * + * @param indentation the indentation level + * @param classGen the StringBuilder for the class code + */ protected void addMethods(int indentation, final StringBuilder classGen) { if (!methods.isEmpty()) { @@ -133,6 +139,12 @@ protected void addMethods(int indentation, final StringBuilder classGen) { } } + /** + * Adds constructors to the class code generation. + * + * @param indentation the indentation level + * @param classGen the StringBuilder for the class code + */ protected void addConstructors(int indentation, final StringBuilder classGen) { if (!constructors.isEmpty()) { final StringBuilder indent1 = Utils.indent(indentation + 1); @@ -143,10 +155,15 @@ protected void addConstructors(int indentation, final StringBuilder classGen) { classGen.append(constStr.trim()); classGen.append(ln()); - // classGen.append(Utils.indent(1)); } } + /** + * Adds fields to the class code generation. + * + * @param indentation the indentation level + * @param classGen the StringBuilder for the class code + */ protected void addFields(int indentation, final StringBuilder classGen) { if (!fields.isEmpty()) { @@ -156,16 +173,16 @@ protected void addFields(int indentation, final StringBuilder classGen) { final String fieldsStr = StringUtils.join(fields, field -> field.generateCode(indentation + 1).toString(), ln()); classGen.append(fieldsStr.trim()); - /* - * for (Field field : fields) { StringBuilder fieldBuf = - * field.generateCode(1); classGen.append(fieldBuf); - * classGen.append("\n"); } - */ classGen.append(ln() + ln()); } } + /** + * Adds implemented interfaces to the class code generation. + * + * @param classGen the StringBuilder for the class code + */ protected void addImplements(final StringBuilder classGen) { if (!interfaces.isEmpty()) { classGen.append(" implements "); @@ -176,7 +193,7 @@ protected void addImplements(final StringBuilder classGen) { } /** - * Add a new interface to the class + * Adds a new interface to the class. * * @param interfaceClass the new interface * @return true if the interface was successfully added @@ -191,7 +208,7 @@ public boolean addInterface(JavaType interfaceClass) { } /** - * Removes a interface from the class + * Removes an interface from the class. * * @param interfaceClass the interface to remove * @return true if the interface was successfully removed @@ -207,9 +224,9 @@ public boolean removeInterface(JavaType interfaceClass) { } /** - * Add a new constructor to the class + * Adds a new constructor to the class. * - * @param constructor the new contructor + * @param constructor the new constructor * @return true if the constructor was successfully added */ public boolean add(Constructor constructor) { @@ -223,9 +240,9 @@ public boolean add(Constructor constructor) { } /** - * Removes a constructor from the class + * Removes a constructor from the class. * - * @param interfaceClass the constructor to remove + * @param constructor the constructor to remove * @return true if the constructor was successfully removed */ public boolean remove(Constructor constructor) { @@ -233,7 +250,7 @@ public boolean remove(Constructor constructor) { } /** - * Add a new field to the class + * Adds a new field to the class. * * @param field the new field * @return true if the field was successfully added @@ -247,7 +264,7 @@ public boolean add(Field field) { } /** - * Removes a field from the class + * Removes a field from the class. * * @param field the field to remove * @return true if the field was successfully removed @@ -257,7 +274,7 @@ public boolean remove(Field field) { } /** - * Add a new method to the class + * Adds a new method to the class. * * @param method the new method * @return true if the method was successfully added @@ -273,7 +290,7 @@ public boolean add(Method method) { } /** - * Removes a method from the class + * Removes a method from the class. * * @param method the method to remove * @return true if the method was successfully removed @@ -290,6 +307,8 @@ public JavaType getSuperClass() { } /** + * Sets the superClass. + * * @param superClass the superClass to set */ public void setSuperClass(JavaType superClass) { @@ -298,9 +317,9 @@ public void setSuperClass(JavaType superClass) { } /** - * Generate the interface code + * Generates the interface code. * - * @return + * @return the generated code */ public StringBuilder generate() { return generateCode(0); @@ -320,24 +339,33 @@ public List getMethods() { return methods; } + /** + * Adds all methods to the class. + * + * @param methods the methods to add + */ public void addAll(Collection methods) { methods.addAll(methods); } + /** + * Creates or retrieves an empty constructor. + * + * @return the empty constructor + */ public Constructor createOrGetEmptyConstructor() { for (final Constructor ctor : constructors) { if (ctor.getArguments().isEmpty()) { return ctor; } } - return new Constructor(this); // already adding the constructor to - // this JavaClass + return new Constructor(this); } /** - * Create a constructor containing all the fields and generates the associated assignment code; + * Creates a constructor containing all the fields and generates the associated assignment code. * - * @return + * @return the full constructor */ public Constructor createFullConstructor() { final Constructor newCtor = new Constructor(this); @@ -346,26 +374,52 @@ public Constructor createFullConstructor() { return newCtor; } + /** + * @return the interfaces + */ public List getInterfaces() { return interfaces; } + /** + * Sets the interfaces. + * + * @param interfaces the interfaces to set + */ public void setInterfaces(List interfaces) { this.interfaces = interfaces; } + /** + * @return the constructors + */ public List getConstructors() { return constructors; } + /** + * Sets the constructors. + * + * @param constructors the constructors to set + */ public void setConstructors(List constructors) { this.constructors = constructors; } + /** + * Sets the fields. + * + * @param fields the fields to set + */ public void setFields(List fields) { this.fields = fields; } + /** + * Sets the methods. + * + * @param methods the methods to set + */ public void setMethods(List methods) { this.methods = methods; } diff --git a/JavaGenerator/src/org/specs/generators/java/classtypes/JavaEnum.java b/JavaGenerator/src/org/specs/generators/java/classtypes/JavaEnum.java index 913c4f22..8647291c 100644 --- a/JavaGenerator/src/org/specs/generators/java/classtypes/JavaEnum.java +++ b/JavaGenerator/src/org/specs/generators/java/classtypes/JavaEnum.java @@ -1,14 +1,14 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.classtypes; @@ -21,28 +21,19 @@ import tdrc.utils.StringUtils; /** - * Java class generation - * + * Represents a Java enum for code generation. Provides methods to manage enum items and generate enum code. + * * @author Tiago - * */ public class JavaEnum extends JavaClass { - // private List interfaces; - - // Fields and methods pertaining to the java class private List items; - // private List fields; - // private List methods; - // private List constructors; /** - * Create a public enum with name and package - * - * @param name - * the name for the enum - * @param classPackage - * the class package + * Create a public enum with name and package. + * + * @param name the name for the enum + * @param classPackage the class package */ public JavaEnum(String name, String classPackage) { super(name, classPackage); @@ -50,15 +41,10 @@ public JavaEnum(String name, String classPackage) { } /** - * Initialize the JavaEnum' lists + * Initialize the JavaEnum' lists. */ private void init() { - - // interfaces = new UniqueList<>(); items = new UniqueList<>(); - // fields = new UniqueList<>(); - // methods = new UniqueList<>(); - // constructors = new UniqueList<>(); } @Override @@ -73,9 +59,8 @@ public JavaType getSuperClass() { /** * Generate the corresponding java enum code, containing the package, imports, items, fields, methods, etc. - * - * @param indentaton - * level of indentation + * + * @param indentation level of indentation * @return the generated java enum code */ @Override @@ -97,7 +82,6 @@ public StringBuilder generateCode(int indentation) { classGen.append(generateClassTail(indentation)); return classGen; - } private void addItems(int indentation, final StringBuilder classGen) { @@ -109,20 +93,18 @@ private void addItems(int indentation, final StringBuilder classGen) { } /** - * Add an enum item to the enum - * - * @param item - * the item to append + * Add an enum item to the enum. + * + * @param item the item to append */ public void add(EnumItem item) { items.add(item); } /** - * Add an enum item by the item name - * - * @param name - * the name for the new Item + * Add an enum item by the item name. + * + * @param name the name for the new Item */ public void addItem(String name) { items.add(new EnumItem(name)); diff --git a/JavaGenerator/src/org/specs/generators/java/enums/Annotation.java b/JavaGenerator/src/org/specs/generators/java/enums/Annotation.java index 4dac8557..d304ba43 100644 --- a/JavaGenerator/src/org/specs/generators/java/enums/Annotation.java +++ b/JavaGenerator/src/org/specs/generators/java/enums/Annotation.java @@ -1,36 +1,44 @@ /** * Copyright 2015 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.enums; +/** + * Enum representing common Java annotations for code generation. + */ public enum Annotation { - OVERRIDE("Override"), DEPRECATED("Deprecated"), SUPPRESSWARNINGS("SuppressWarnings"), SAFEVARARGS( - "SafeVarargs"), FUNCTIONALINTERFACE("FunctionalInterface"), TARGET("Target"),; + OVERRIDE("Override"), DEPRECATED("Deprecated"), SUPPRESSWARNINGS("SuppressWarnings"), SAFEVARARGS( + "SafeVarargs"), FUNCTIONALINTERFACE("FunctionalInterface"), TARGET("Target"),; - private String tag; - private final String AtSign = "@"; + private String tag; + private final String AtSign = "@"; - Annotation(String tag) { - this.tag = tag; - } + Annotation(String tag) { + this.tag = tag; + } - public String getTag() { - return AtSign + tag; - } + /** + * Returns the annotation tag with '@' prefix. + * + * @return the annotation tag + */ + public String getTag() { + return AtSign + tag; + } - @Override - public String toString() { - return getTag(); - } + @Override + public String toString() { + return getTag(); + } } diff --git a/JavaGenerator/src/org/specs/generators/java/enums/JDocTag.java b/JavaGenerator/src/org/specs/generators/java/enums/JDocTag.java index ca571f9f..4f418498 100644 --- a/JavaGenerator/src/org/specs/generators/java/enums/JDocTag.java +++ b/JavaGenerator/src/org/specs/generators/java/enums/JDocTag.java @@ -1,32 +1,48 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.enums; /** - * Tags used in javadoc - * + * Enum representing tags used in JavaDoc comments. + * * @author Tiago */ public enum JDocTag { - AUTHOR("@author"), CATEGORY("@category"), DEPRECATED("@deprecated"), SEE("@see"), VERSION("@version"), PARAM( - "@param"), RETURN("@return"),; - private String tag; + AUTHOR("@author"), + CATEGORY("@category"), + DEPRECATED("@deprecated"), + SEE("@see"), + VERSION("@version"), + PARAM("@param"), + RETURN("@return"); - JDocTag(String tag) { - this.tag = tag; - } + private String tag; - public String getTag() { - return tag; - } + /** + * Constructor for JDocTag. + * + * @param tag the JavaDoc tag string + */ + JDocTag(String tag) { + this.tag = tag; + } + + /** + * Returns the JavaDoc tag string. + * + * @return the tag string + */ + public String getTag() { + return tag; + } } \ No newline at end of file diff --git a/JavaGenerator/src/org/specs/generators/java/enums/Modifier.java b/JavaGenerator/src/org/specs/generators/java/enums/Modifier.java index 40e7dd48..fdd68657 100644 --- a/JavaGenerator/src/org/specs/generators/java/enums/Modifier.java +++ b/JavaGenerator/src/org/specs/generators/java/enums/Modifier.java @@ -1,38 +1,52 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.enums; /** - * Modifiers for the class/field/method - * + * Enum representing Java modifiers for classes, fields, and methods. + * * @author Tiago - * */ public enum Modifier { - ABSTRACT("abstract"), STATIC("static"), FINAL("final"); + ABSTRACT("abstract"), STATIC("static"), FINAL("final"); - Modifier(String type) { - this.type = type; - } + /** + * Constructor for the Modifier enum. + * + * @param type the string representation of the modifier + */ + Modifier(String type) { + this.type = type; + } - private String type; + private String type; - public String getType() { - return type; - } + /** + * Returns the string representation of the modifier. + * + * @return the modifier string + */ + public String getType() { + return type; + } - @Override - public String toString() { - return type; - } + /** + * Returns the string representation of the modifier. + * + * @return the modifier string + */ + @Override + public String toString() { + return type; + } } \ No newline at end of file diff --git a/JavaGenerator/src/org/specs/generators/java/enums/NumeralType.java b/JavaGenerator/src/org/specs/generators/java/enums/NumeralType.java index e72447af..f0665d75 100644 --- a/JavaGenerator/src/org/specs/generators/java/enums/NumeralType.java +++ b/JavaGenerator/src/org/specs/generators/java/enums/NumeralType.java @@ -1,47 +1,58 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.enums; /** - * Types requiring numerical return - * + * Enum representing types requiring numerical return for code generation. + * * @author Tiago @ */ public enum NumeralType { - INT(int.class.getName()), DOUBLE(double.class.getName()), FLOAT(float.class.getName()), LONG( - long.class.getName()), SHORT(short.class.getName()), BYTE(byte.class.getName()); + INT(int.class.getName()), DOUBLE(double.class.getName()), FLOAT(float.class.getName()), LONG( + long.class.getName()), SHORT(short.class.getName()), BYTE(byte.class.getName()); - NumeralType(String type) { - this.type = type; - } + NumeralType(String type) { + this.type = type; + } - private String type; + private String type; - public String getType() { - return type; - } + /** + * Returns the string representation of the numeral type. + * + * @return the type string + */ + public String getType() { + return type; + } - @Override - public String toString() { - return type; - } + @Override + public String toString() { + return type; + } - public static boolean contains(String type) { - for (final NumeralType nt : values()) { - if (nt.type.equals(type)) { - return true; - } - } - return false; - } + /** + * Checks if the given type is contained in the enum. + * + * @param type the type to check + * @return true if the type is contained, false otherwise + */ + public static boolean contains(String type) { + for (final NumeralType nt : values()) { + if (nt.type.equals(type)) { + return true; + } + } + return false; + } } \ No newline at end of file diff --git a/JavaGenerator/src/org/specs/generators/java/enums/ObjectOfPrimitives.java b/JavaGenerator/src/org/specs/generators/java/enums/ObjectOfPrimitives.java index 348e616c..03b1dd2c 100644 --- a/JavaGenerator/src/org/specs/generators/java/enums/ObjectOfPrimitives.java +++ b/JavaGenerator/src/org/specs/generators/java/enums/ObjectOfPrimitives.java @@ -1,51 +1,71 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.enums; +/** + * Enum representing object types for Java primitives for code generation. + */ public enum ObjectOfPrimitives { - INTEGER(Integer.class.getSimpleName()), DOUBLE(Double.class.getSimpleName()), FLOAT( - Float.class.getSimpleName()), LONG(Long.class.getSimpleName()), SHORT(Short.class.getSimpleName()), BYTE( - Byte.class.getSimpleName()), BOOLEAN(Boolean.class.getSimpleName()); + INTEGER(Integer.class.getSimpleName()), DOUBLE(Double.class.getSimpleName()), FLOAT( + Float.class.getSimpleName()), LONG(Long.class.getSimpleName()), SHORT(Short.class.getSimpleName()), BYTE( + Byte.class.getSimpleName()), BOOLEAN(Boolean.class.getSimpleName()); - ObjectOfPrimitives(String type) { - this.type = type; - } + ObjectOfPrimitives(String type) { + this.type = type; + } - private String type; + private String type; - public String getType() { - return type; - } + /** + * Returns the string representation of the object type. + * + * @return the type string + */ + public String getType() { + return type; + } - public static String getPrimitive(String type) { - final ObjectOfPrimitives object = ObjectOfPrimitives.valueOf(type.toUpperCase()); - if (object == INTEGER) { - return int.class.getName(); - } - return object.type.toLowerCase(); - } + /** + * Returns the primitive type name corresponding to the given object type. + * + * @param type the object type + * @return the primitive type name + */ + public static String getPrimitive(String type) { + final ObjectOfPrimitives object = ObjectOfPrimitives.valueOf(type.toUpperCase()); + if (object == INTEGER) { + return int.class.getName(); + } + return object.type.toLowerCase(); + } - @Override - public String toString() { - return type; - } + @Override + public String toString() { + return type; + } - public static boolean contains(String type) { - for (final ObjectOfPrimitives nt : values()) { - if (nt.type.equals(type)) { - return true; - } - } - return false; - } + /** + * Checks if the given type is contained in the enum. + * + * @param type the type to check + * @return true if the type is contained, false otherwise + */ + public static boolean contains(String type) { + for (final ObjectOfPrimitives nt : values()) { + if (nt.type.equals(type)) { + return true; + } + } + return false; + } } diff --git a/JavaGenerator/src/org/specs/generators/java/enums/Privacy.java b/JavaGenerator/src/org/specs/generators/java/enums/Privacy.java index 559e738d..bfd84c42 100644 --- a/JavaGenerator/src/org/specs/generators/java/enums/Privacy.java +++ b/JavaGenerator/src/org/specs/generators/java/enums/Privacy.java @@ -1,38 +1,52 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.enums; /** - * Privacy level for the class/field/method - * + * Enum representing privacy levels for classes, fields, and methods in Java. + * * @author Tiago - * */ public enum Privacy { - PUBLIC("public"), PRIVATE("private"), PROTECTED("protected"), PACKAGE_PROTECTED(""); + PUBLIC("public"), PRIVATE("private"), PROTECTED("protected"), PACKAGE_PROTECTED(""); - Privacy(String type) { - this.type = type; - } + /** + * Constructor for the Privacy enum. + * + * @param type the string representation of the privacy level + */ + Privacy(String type) { + this.type = type; + } - private String type; + private String type; - public String getType() { - return type; - } + /** + * Returns the string representation of the privacy level. + * + * @return the privacy string + */ + public String getType() { + return type; + } - @Override - public String toString() { - return type; - } + /** + * Returns the string representation of the privacy level. + * + * @return the privacy string + */ + @Override + public String toString() { + return type; + } } diff --git a/JavaGenerator/src/org/specs/generators/java/exprs/GenericExpression.java b/JavaGenerator/src/org/specs/generators/java/exprs/GenericExpression.java index 6003f598..863dfb2f 100644 --- a/JavaGenerator/src/org/specs/generators/java/exprs/GenericExpression.java +++ b/JavaGenerator/src/org/specs/generators/java/exprs/GenericExpression.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.exprs; @@ -16,28 +16,49 @@ import org.specs.generators.java.utils.Utils; /** - * Generic Implementation of an expression which accepts a string and outputs the exactly same string - * - * @author Tiago + * Generic implementation of a Java expression that outputs the provided string as-is. * + * @author Tiago */ public class GenericExpression implements IExpression { String expression; + /** + * Constructs a GenericExpression with the given string. + * + * @param expression the string representing the expression + */ public GenericExpression(String expression) { this.expression = expression; } + /** + * Creates a GenericExpression from a string. + * + * @param expression the string representing the expression + * @return a new GenericExpression instance + */ public static GenericExpression fromString(String expression) { return new GenericExpression(expression); } + /** + * Generates the code for this expression with the specified indentation. + * + * @param indentation the indentation level + * @return a StringBuilder containing the generated code + */ @Override public StringBuilder generateCode(int indentation) { return new StringBuilder(Utils.indent(indentation) + expression); } + /** + * Returns the string representation of this expression. + * + * @return the generated code as a string + */ @Override public String toString() { return generateCode(0).toString(); diff --git a/JavaGenerator/src/org/specs/generators/java/exprs/IExpression.java b/JavaGenerator/src/org/specs/generators/java/exprs/IExpression.java index 25284814..98ce9358 100644 --- a/JavaGenerator/src/org/specs/generators/java/exprs/IExpression.java +++ b/JavaGenerator/src/org/specs/generators/java/exprs/IExpression.java @@ -1,20 +1,24 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.exprs; import org.specs.generators.java.IGenerate; +/** + * Interface representing a Java expression for code generation. + * Extends {@link IGenerate} to provide code generation capabilities for expressions. + */ public interface IExpression extends IGenerate { } diff --git a/JavaGenerator/src/org/specs/generators/java/members/Argument.java b/JavaGenerator/src/org/specs/generators/java/members/Argument.java index a9f95d24..7cf9c06e 100644 --- a/JavaGenerator/src/org/specs/generators/java/members/Argument.java +++ b/JavaGenerator/src/org/specs/generators/java/members/Argument.java @@ -1,78 +1,92 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.members; import org.specs.generators.java.types.JavaType; /** - * Argument declaration for a method - * + * Represents an argument declaration for a method. + * * @author Tiago - * */ public class Argument { - private String name; - private JavaType classType; + private String name; + private JavaType classType; - /** - * Create an Argument with type classType - * - * @param classType - * @param name - */ - public Argument(JavaType classType, String name) { - setName(name); - setClassType(classType); - } + /** + * Creates an Argument with the specified type and name. + * + * @param classType the type of the argument + * @param name the name of the argument + */ + public Argument(JavaType classType, String name) { + setName(name); + setClassType(classType); + } - /** - * @return the classType - */ - public JavaType getClassType() { - return classType; - } + /** + * Returns the type of the argument. + * + * @return the argument type + */ + public JavaType getClassType() { + return classType; + } - /** - * @param classType - * the classType to set - */ - public void setClassType(JavaType classType) { - this.classType = classType; - } + /** + * Sets the type of the argument. + * + * @param classType the argument type to set + */ + public void setClassType(JavaType classType) { + this.classType = classType; + } - /** - * @return the name - */ - public String getName() { - return name; - } + /** + * Returns the name of the argument. + * + * @return the argument name + */ + public String getName() { + return name; + } - /** - * @param name - * the name to set - */ - public void setName(String name) { - this.name = name; - } + /** + * Sets the name of the argument. + * + * @param name the argument name to set + */ + public void setName(String name) { + this.name = name; + } - @Override - public String toString() { - return classType.getSimpleType() + " " + name; - } + /** + * Returns a string representation of the argument. + * + * @return the argument as a string + */ + @Override + public String toString() { + return classType.getSimpleType() + " " + name; + } - @Override - public Argument clone() { - - return new Argument(classType.clone(), name); - } + /** + * Creates a clone of this argument. + * + * @return a new Argument instance with the same type and name + */ + @Override + public Argument clone() { + return new Argument(classType.clone(), name); + } } diff --git a/JavaGenerator/src/org/specs/generators/java/members/Constructor.java b/JavaGenerator/src/org/specs/generators/java/members/Constructor.java index f54d9284..f3ab4d6f 100644 --- a/JavaGenerator/src/org/specs/generators/java/members/Constructor.java +++ b/JavaGenerator/src/org/specs/generators/java/members/Constructor.java @@ -1,14 +1,14 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.members; @@ -28,10 +28,9 @@ import tdrc.utils.StringUtils; /** - * Constructor declaration for a {@link JavaClass} - * + * Represents a constructor declaration for a Java class or enum. + * * @author Tiago - * */ public class Constructor implements IGenerate { private Privacy privacy; @@ -42,10 +41,9 @@ public class Constructor implements IGenerate { private StringBuffer methodBody; /** - * Generate a public, empty constructor for the {@link JavaClass} - * - * @param javaClass - * the class pertaining the constructor + * Generates a public, empty constructor for the specified Java class. + * + * @param javaClass the class pertaining to the constructor */ public Constructor(JavaClass javaClass) { this.javaClass = javaClass; @@ -54,10 +52,9 @@ public Constructor(JavaClass javaClass) { } /** - * Generate a private, empty constructor for the {@link JavaEnum} + * Generates a private, empty constructor for the specified Java enum. * - * @param javaEnum - * the enum pertaining the constructor + * @param javaEnum the enum pertaining to the constructor */ public Constructor(JavaEnum javaEnum) { this.javaEnum = javaEnum; @@ -66,9 +63,9 @@ public Constructor(JavaEnum javaEnum) { } /** - * Initialize Constructor instance - * - * @param javaClass + * Initializes the Constructor instance. + * + * @param javaClass the class pertaining to the constructor */ private void init(JavaClass javaClass) { arguments = new ArrayList<>(); @@ -78,12 +75,10 @@ private void init(JavaClass javaClass) { } /** - * Generate a empty constructor, with required privacy, for the {@link JavaClass} - * - * @param privacy - * the privacy level - * @param javaClass - * the class pertaining the constructor + * Generates an empty constructor with the required privacy for the specified Java class. + * + * @param privacy the privacy level + * @param javaClass the class pertaining to the constructor */ public Constructor(Privacy privacy, JavaClass javaClass) { this.javaClass = javaClass; @@ -92,10 +87,10 @@ public Constructor(Privacy privacy, JavaClass javaClass) { } /** - * Add a new argument to the constructor's arguments - * - * @param the - * new modifier + * Adds a new argument to the constructor's arguments. + * + * @param classType the type of the argument + * @param name the name of the argument */ public void addArgument(JavaType classType, String name) { final Argument newArg = new Argument(classType, name); @@ -103,10 +98,9 @@ public void addArgument(JavaType classType, String name) { } /** - * Add a new argument based on a field - * - * @param the - * new modifier + * Adds a new argument based on a field. + * + * @param field the field to add as an argument */ public void addArgument(Field field) { final Argument newArg = new Argument(field.getType(), field.getName()); @@ -114,21 +108,19 @@ public void addArgument(Field field) { } /** - * Add a list of field as argument - * - * @param the - * new modifier + * Adds a list of fields as arguments. + * + * @param field the collection of fields to add as arguments */ public void addArguments(Collection field) { field.forEach(this::addArgument); } /** - * Generate java source based on the privacy, arguments and name - * - * @param indentiation - * the code indentation - * @return the generated java constructor code + * Generates Java source code based on the privacy, arguments, and name. + * + * @param indentation the code indentation + * @return the generated Java constructor code */ @Override public StringBuilder generateCode(int indentation) { @@ -156,20 +148,15 @@ public StringBuilder generateCode(int indentation) { final String bodyCode = methodBody.toString().replace(ln(), ln() + indent).trim(); constructorStr.append(bodyCode); constructorStr.append(ln() + indent0); - } else { - // constructorStr.append("// TODO Auto-generated constructor - // stub\n"); - // constructorStr.append(Utils.indent(indentation)); } constructorStr.append("}"); return constructorStr; } /** - * Append text to the javadoc comment - * - * @param comment - * the text to append + * Appends text to the Javadoc comment. + * + * @param comment the text to append * @return the {@link StringBuilder} with the new comment */ public StringBuilder appendComment(String comment) { @@ -177,22 +164,19 @@ public StringBuilder appendComment(String comment) { } /** - * Add a new javadoc tag to the comment, with no description - * - * @param tag - * the new tag to add + * Adds a new Javadoc tag to the comment, with no description. + * + * @param tag the new tag to add */ public void addJavaDocTag(JDocTag tag) { javaDocComment.addTag(tag); } /** - * Add a new javadoc tag to the comment with description - * - * @param tag - * the new tag to add - * @param description - * the tag description + * Adds a new Javadoc tag to the comment with a description. + * + * @param tag the new tag to add + * @param description the tag description */ public void addJavaDocTag(JDocTag tag, String description) { javaDocComment.addTag(tag, description); @@ -204,90 +188,91 @@ public String toString() { } /** - * @return the privacy + * @return the privacy level */ public Privacy getPrivacy() { return privacy; } /** - * @param privacy - * the privacy to set + * Sets the privacy level. + * + * @param privacy the privacy to set */ public void setPrivacy(Privacy privacy) { this.privacy = privacy; } /** - * @return the javaClass + * @return the Java class */ public JavaClass getJavaClass() { return javaClass; } /** - * @param javaClass - * the javaClass to set + * Sets the Java class. + * + * @param javaClass the Java class to set */ public void setJavaClass(JavaClass javaClass) { this.javaClass = javaClass; } /** - * @return the arguments + * @return the list of arguments */ public List getArguments() { return arguments; } /** - * @param arguments - * the arguments to set + * Sets the list of arguments. + * + * @param arguments the arguments to set */ public void setArguments(List arguments) { this.arguments = arguments; } /** - * @return the methodBody + * @return the method body */ public StringBuffer getMethodBody() { return methodBody; } /** - * @param methodBody - * the methodBody to set + * Sets the method body. + * + * @param methodBody the method body to set */ public void setMethodBody(StringBuffer methodBody) { this.methodBody = methodBody; } /** - * Append code to the method body - * - * @param code - * the code to append + * Appends code to the method body. + * + * @param code the code to append */ public void appendCode(String code) { methodBody.append(code); } /** - * Append code to the method body - * - * @param code - * the code to append + * Appends code to the method body. + * + * @param code the code to append */ public void appendCode(StringBuffer code) { methodBody.append(code); } /** - * Append default code based on the parameters of the constructor - * - * @param useSetters - * use the set methods instead of assignments; + * Appends default code based on the parameters of the constructor. + * + * @param useSetters use the set methods instead of assignments */ public void appendDefaultCode(boolean useSetters) { Consumer generateAssignment; @@ -303,6 +288,9 @@ public void appendDefaultCode(boolean useSetters) { arguments.forEach(generateAssignment); } + /** + * Clears the method body. + */ public void clearCode() { methodBody.delete(0, methodBody.length()); } diff --git a/JavaGenerator/src/org/specs/generators/java/members/EnumItem.java b/JavaGenerator/src/org/specs/generators/java/members/EnumItem.java index ade1f157..c276a6e1 100644 --- a/JavaGenerator/src/org/specs/generators/java/members/EnumItem.java +++ b/JavaGenerator/src/org/specs/generators/java/members/EnumItem.java @@ -1,17 +1,14 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. - */ -/** - * + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.members; @@ -22,108 +19,128 @@ import org.specs.generators.java.utils.Utils; /** - * An item for a java Enum - * + * Represents an item for a Java enum, including its name and parameters. + * * @author Tiago Carvalho - * */ public class EnumItem implements IGenerate { - private String name; - private List parameters; - - public EnumItem(String name) { - this.name = name; - parameters = new ArrayList<>(); - } + private String name; + private List parameters; - /** - * Add a parameter to the item - * - * @param value - * the value to add - */ - public void addParameter(String value) { - parameters.add(value); - } + /** + * Constructs an EnumItem with the specified name. + * + * @param name the name of the enum item + */ + public EnumItem(String name) { + this.name = name; + parameters = new ArrayList<>(); + } - /** - * @return the name - */ - public String getName() { - return name; - } + /** + * Adds a parameter to the enum item. + * + * @param value the value to add + */ + public void addParameter(String value) { + parameters.add(value); + } - /** - * @param name - * the name to set - */ - public void setName(String name) { - this.name = name; - } + /** + * Returns the name of the enum item. + * + * @return the name + */ + public String getName() { + return name; + } - /** - * @return the parameters - */ - public List getParameters() { - return parameters; - } + /** + * Sets the name of the enum item. + * + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } - /** - * @param parameters - * the parameters to set - */ - public void setParameters(List parameters) { - this.parameters = parameters; - } + /** + * Returns the parameters of the enum item. + * + * @return the parameters + */ + public List getParameters() { + return parameters; + } - /* - * @Override public boolean equals(Object arg0) { if (!(arg0 instanceof - * EnumItem)) return false; EnumItem clone = (EnumItem) arg0; return - * clone.name.equals(this.name); } - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - return result; - } + /** + * Sets the parameters of the enum item. + * + * @param parameters the parameters to set + */ + public void setParameters(List parameters) { + this.parameters = parameters; + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final EnumItem other = (EnumItem) obj; - if (name == null) { - if (other.name != null) { - return false; - } - } else if (!name.equals(other.name)) { - return false; - } - return true; - } + /** + * Returns the hash code for this enum item. + * + * @return the hash code + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } - @Override - public StringBuilder generateCode(int indentation) { - final StringBuilder itemBuilder = Utils.indent(indentation); - itemBuilder.append(name); - if (!parameters.isEmpty()) { - itemBuilder.append("("); - for (final String param : parameters) { - itemBuilder.append(param + ", "); - } - itemBuilder.replace(itemBuilder.length() - 2, itemBuilder.length(), ")"); - } + /** + * Checks if this enum item is equal to another object. + * + * @param obj the object to compare + * @return true if equal, false otherwise + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final EnumItem other = (EnumItem) obj; + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + return true; + } - return itemBuilder; - } + /** + * Generates the code representation of this enum item with the specified indentation. + * + * @param indentation the level of indentation + * @return the generated code as a StringBuilder + */ + @Override + public StringBuilder generateCode(int indentation) { + final StringBuilder itemBuilder = Utils.indent(indentation); + itemBuilder.append(name); + if (!parameters.isEmpty()) { + itemBuilder.append("("); + for (final String param : parameters) { + itemBuilder.append(param + ", "); + } + itemBuilder.replace(itemBuilder.length() - 2, itemBuilder.length(), ")"); + } + return itemBuilder; + } } diff --git a/JavaGenerator/src/org/specs/generators/java/members/Field.java b/JavaGenerator/src/org/specs/generators/java/members/Field.java index 21268d0a..9ef6acfe 100644 --- a/JavaGenerator/src/org/specs/generators/java/members/Field.java +++ b/JavaGenerator/src/org/specs/generators/java/members/Field.java @@ -1,14 +1,14 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.members; @@ -26,10 +26,9 @@ import org.specs.generators.java.utils.Utils; /** - * Field declaration for a {@link JavaClass} field - * + * Represents a field declaration for a Java class. + * * @author Tiago - * */ public class Field implements IGenerate { private Privacy privacy; @@ -41,26 +40,21 @@ public class Field implements IGenerate { private IExpression initializer; /** - * Generate a private field of type classType - * - * @param classType - * the class of the field - * @param name - * the name for the field + * Generates a private field of the specified type and name. + * + * @param classType the type of the field + * @param name the name of the field */ public Field(JavaType classType, String name) { init(classType, name, Privacy.PRIVATE); } /** - * Generate a field of type classType with the chosen privacy - * - * @param classType - * the class of the field - * @param name - * the name for the field - * @param privacy - * the privacy level + * Generates a field of the specified type, name, and privacy level. + * + * @param classType the type of the field + * @param name the name of the field + * @param privacy the privacy level */ public Field(JavaType classType, String name, Privacy privacy) { init(classType, name, privacy); @@ -77,10 +71,9 @@ private void init(JavaType classType, String name, Privacy privacy) { } /** - * Add a new modifier to the field - * - * @param newMod - * the new modifier + * Adds a new modifier to the field. + * + * @param newMod the new modifier */ public void addModifier(Modifier newMod) { if (!modifiers.contains(newMod)) { @@ -89,10 +82,9 @@ public void addModifier(Modifier newMod) { } /** - * Add a new annotation to the class - * - * @param annotation - * the new annotation + * Adds a new annotation to the field. + * + * @param annotation the new annotation * @return true if the annotation was successfully added */ public boolean add(Annotation annotation) { @@ -100,10 +92,9 @@ public boolean add(Annotation annotation) { } /** - * Removes a annotation from the class - * - * @param annotation - * the annotation to remove + * Removes an annotation from the field. + * + * @param annotation the annotation to remove * @return true if the annotation was successfully removed */ public boolean remove(Annotation annotation) { @@ -111,11 +102,10 @@ public boolean remove(Annotation annotation) { } /** - * Generate java source based on the field's privacy, modifiers, class and name - * - * @param indentation - * the code indentation - * @return + * Generates Java source code based on the field's privacy, modifiers, type, and name. + * + * @param indentation the code indentation + * @return the generated code as a StringBuilder */ @Override public StringBuilder generateCode(int indentation) { @@ -157,76 +147,88 @@ public String toString() { } /** - * @return the privacy + * @return the privacy level of the field */ public Privacy getPrivacy() { return privacy; } /** - * @param privacy - * the privacy to set + * Sets the privacy level of the field. + * + * @param privacy the privacy level to set */ public void setPrivacy(Privacy privacy) { this.privacy = privacy; } /** - * @return the name + * @return the name of the field */ public String getName() { return name; } /** - * @param name - * the name to set + * Sets the name of the field. + * + * @param name the name to set */ public void setName(String name) { this.name = name; } /** - * @return the classType + * @return the type of the field */ public JavaType getType() { return classType; } /** - * @param classType - * the classType to set + * Sets the type of the field. + * + * @param classType the type to set */ public void setType(JavaType classType) { this.classType = classType; } /** - * @return the modifiers + * @return the list of modifiers applied to the field */ public List getModifiers() { return modifiers; } /** - * @return the defaultInitializer + * @return true if the field has a default initializer, false otherwise */ public boolean isDefaultInitializer() { return defaultInitializer; } /** - * @param defaultInitializer - * the defaultInitializer to set + * Sets whether the field has a default initializer. + * + * @param defaultInitializer the default initializer flag to set */ public void setDefaultInitializer(boolean defaultInitializer) { this.defaultInitializer = defaultInitializer; } + /** + * @return the initializer expression of the field + */ public IExpression getInitializer() { return initializer; } + /** + * Sets the initializer expression of the field. + * + * @param initializer the initializer expression to set + */ public void setInitializer(IExpression initializer) { this.initializer = initializer; } diff --git a/JavaGenerator/src/org/specs/generators/java/members/JavaDoc.java b/JavaGenerator/src/org/specs/generators/java/members/JavaDoc.java index 28e16dfe..0c6dc8a0 100644 --- a/JavaGenerator/src/org/specs/generators/java/members/JavaDoc.java +++ b/JavaGenerator/src/org/specs/generators/java/members/JavaDoc.java @@ -1,14 +1,14 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.members; @@ -20,10 +20,9 @@ import org.specs.generators.java.utils.Utils; /** - * Generate a comment used for a java document - * + * Represents a JavaDoc comment for code generation, including tags and descriptions. + * * @author Tiago - * */ public class JavaDoc implements IGenerate { @@ -31,7 +30,7 @@ public class JavaDoc implements IGenerate { private final List tags; /** - * Empty constructor + * Constructs an empty JavaDoc comment. */ public JavaDoc() { tags = new ArrayList<>(); @@ -39,10 +38,9 @@ public JavaDoc() { } /** - * Create a javadoc comment with a predefined {@link StringBuilder} - * - * @param comment - * the {@link StringBuilder} containing the comment + * Constructs a JavaDoc comment with a predefined StringBuilder. + * + * @param comment the StringBuilder containing the comment */ public JavaDoc(StringBuilder comment) { tags = new ArrayList<>(); @@ -50,51 +48,57 @@ public JavaDoc(StringBuilder comment) { } /** - * Create a javadoc comment with a predefined {@link String} - * - * @param comment - * the {@link String} containing the comment + * Constructs a JavaDoc comment with a predefined String. + * + * @param comment the String containing the comment */ public JavaDoc(String comment) { tags = new ArrayList<>(); setComment(new StringBuilder(comment)); } + /** + * Appends a string to the current comment. + * + * @param comment the string to append + * @return the updated StringBuilder + */ public StringBuilder appendComment(String comment) { return this.comment.append(comment); } /** - * Add a tag with no description - * - * @param tag - * the new tag for the comment + * Adds a tag with no description. + * + * @param tag the new tag for the comment */ public void addTag(JDocTag tag) { tags.add(new JavaDocTag(tag)); } /** - * Add a tag with description - * - * @param tag - * the new tag for the comment + * Adds a tag with a string description. + * + * @param tag the new tag for the comment + * @param descriptionStr the description string */ public void addTag(JDocTag tag, String descriptionStr) { tags.add(new JavaDocTag(tag, descriptionStr)); } /** - * Add a tag with description - * - * @param tag - * the new tag for the comment + * Adds a tag with a StringBuilder description. + * + * @param tag the new tag for the comment + * @param description the description as a StringBuilder */ public void addTag(JDocTag tag, StringBuilder description) { tags.add(new JavaDocTag(tag, description)); } /** + * Returns the comment StringBuilder. + * * @return the comment */ public StringBuilder getComment() { @@ -102,18 +106,18 @@ public StringBuilder getComment() { } /** - * @param comment - * the comment to set + * Sets the comment StringBuilder. + * + * @param comment the comment to set */ public void setComment(StringBuilder comment) { this.comment = comment; } /** - * Remove a tag from the list of tags - * - * @param index - * the position of the tag + * Removes a tag from the list of tags. + * + * @param index the position of the tag * @return the removed tag */ public JavaDocTag removeTag(int index) { @@ -121,21 +125,20 @@ public JavaDocTag removeTag(int index) { } /** - * Get a tag in the position index - * - * @param index - * the position of the tag - * @return + * Gets a tag in the position index. + * + * @param index the position of the tag + * @return the tag at the specified index */ public JavaDocTag getTag(int index) { return tags.get(index); } /** - * Generate a javadoc comment with the specified indentation - * - * @param indentation - * @return + * Generates a javadoc comment with the specified indentation. + * + * @param indentation the level of indentation + * @return the generated javadoc comment */ @Override public StringBuilder generateCode(int indentation) { @@ -161,9 +164,13 @@ public StringBuilder generateCode(int indentation) { return commentBuilder; } + /** + * Clones the current JavaDoc instance. + * + * @return a new JavaDoc instance with the same content + */ @Override public JavaDoc clone() { - final JavaDoc javaDoc = new JavaDoc(new StringBuilder(comment)); tags.forEach(t -> javaDoc.addTag(t.getTag(), t.getDescription())); return javaDoc; diff --git a/JavaGenerator/src/org/specs/generators/java/members/JavaDocTag.java b/JavaGenerator/src/org/specs/generators/java/members/JavaDocTag.java index 2d68a9dd..eb83985f 100644 --- a/JavaGenerator/src/org/specs/generators/java/members/JavaDocTag.java +++ b/JavaGenerator/src/org/specs/generators/java/members/JavaDocTag.java @@ -1,14 +1,14 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.members; @@ -17,122 +17,124 @@ import org.specs.generators.java.utils.Utils; /** - * Association between a javadoc tag and its description - * + * Represents an association between a JavaDoc tag and its description for code generation. + * * @author Tiago - * */ public class JavaDocTag implements IGenerate { - // The tag name and its description - private JDocTag tag; - private StringBuilder description; + // The tag name and its description + private JDocTag tag; + private StringBuilder description; - /** - * Add a new Tag with no comment - * - * @param tag - * the javadoc tag - */ - public JavaDocTag(JDocTag tag) { - setTag(tag); - setDescription(new StringBuilder()); - } + /** + * Adds a new tag with no comment. + * + * @param tag the JavaDoc tag + */ + public JavaDocTag(JDocTag tag) { + setTag(tag); + setDescription(new StringBuilder()); + } - /** - * Add a new Tag with the comment inside the {@link String} - * - * @param tag - * the javadoc tag - * @param description - * the {@link String} containing the description - */ - public JavaDocTag(JDocTag tag, String descriptionStr) { - setTag(tag); - setDescription(new StringBuilder(descriptionStr)); - } + /** + * Adds a new tag with a string description. + * + * @param tag the JavaDoc tag + * @param descriptionStr the description string + */ + public JavaDocTag(JDocTag tag, String descriptionStr) { + setTag(tag); + setDescription(new StringBuilder(descriptionStr)); + } - /** - * Add a new Tag with the comment inside the {@link String} - * - * @param tag - * the javadoc tag - * @param description - * the {@link String} containing the description - */ - public JavaDocTag(JDocTag tag, StringBuilder description) { - setTag(tag); - setDescription(description); - } + /** + * Add a new Tag with the comment inside the {@link StringBuilder}. + * + * @param tag the JavaDoc tag + * @param description the {@link StringBuilder} containing the description + */ + public JavaDocTag(JDocTag tag, StringBuilder description) { + setTag(tag); + setDescription(description); + } - /** - * Append a {@link StringBuilder} to the description - * - * @param description - * the {@link StringBuilder} to append - */ - public void append(StringBuilder description) { - this.description.append(description); - } + /** + * Append a {@link StringBuilder} to the description. + * + * @param description the {@link StringBuilder} to append + */ + public void append(StringBuilder description) { + this.description.append(description); + } - /** - * Append a {@link String} to the description - * - * @param descriptionStr - * the {@link String} to append - */ - public void append(String descriptionStr) { - description.append(descriptionStr); - } + /** + * Append a {@link String} to the description. + * + * @param descriptionStr the {@link String} to append + */ + public void append(String descriptionStr) { + description.append(descriptionStr); + } - /** - * @return the description - */ - public StringBuilder getDescription() { - return description; - } + /** + * Gets the description. + * + * @return the description + */ + public StringBuilder getDescription() { + return description; + } - /** - * @param description - * the description to set - */ - public void setDescription(StringBuilder description) { - this.description = description; - } + /** + * Sets the description. + * + * @param description the description to set + */ + public void setDescription(StringBuilder description) { + this.description = description; + } - /** - * @return the tag - */ - public JDocTag getTag() { - return tag; - } + /** + * Gets the tag. + * + * @return the tag + */ + public JDocTag getTag() { + return tag; + } - /** - * @param tag - * the tag to set - */ - public void setTag(JDocTag tag) { - this.tag = tag; - } + /** + * Sets the tag. + * + * @param tag the tag to set + */ + public void setTag(JDocTag tag) { + this.tag = tag; + } - /** - * Generate the javadoc tag - * - * @param indentation - * level of indentation - * @return the generated javadoc tag - */ - @Override - public StringBuilder generateCode(int indentation) { - final StringBuilder generated = Utils.indent(indentation); - generated.append(tag.getTag()); - generated.append(" "); - generated.append(description); - return generated; - } + /** + * Generates the JavaDoc tag. + * + * @param indentation level of indentation + * @return the generated JavaDoc tag + */ + @Override + public StringBuilder generateCode(int indentation) { + final StringBuilder generated = Utils.indent(indentation); + generated.append(tag.getTag()); + generated.append(" "); + generated.append(description); + return generated; + } - @Override - public String toString() { - return generateCode(0).toString(); - } + /** + * Converts the object to a string representation. + * + * @return the string representation of the object + */ + @Override + public String toString() { + return generateCode(0).toString(); + } } diff --git a/JavaGenerator/src/org/specs/generators/java/members/Method.java b/JavaGenerator/src/org/specs/generators/java/members/Method.java index 517d2800..226762d9 100644 --- a/JavaGenerator/src/org/specs/generators/java/members/Method.java +++ b/JavaGenerator/src/org/specs/generators/java/members/Method.java @@ -1,14 +1,14 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.members; @@ -31,10 +31,9 @@ import tdrc.utils.StringUtils; /** - * Method declaration for a {@link JavaClass} - * + * Represents a method declaration for a Java class, including return type, arguments, and modifiers. + * * @author Tiago - * */ public class Method implements IGenerate { @@ -49,26 +48,21 @@ public class Method implements IGenerate { private StringBuffer methodBody; /** - * Generate a public method with return type returnType - * - * @param returnType - * the return of the method - * @param name - * the name for the method + * Generates a public method with the specified return type and name. + * + * @param returnType the return type of the method + * @param name the name of the method */ public Method(JavaType returnType, String name) { init(returnType, name); } /** - * Generate a method of return type returnType with the chosen privacy - * - * @param returnType - * the return of the method - * @param name - * the name for the method - * @param privacy - * the privacy level + * Generates a method with the specified return type, name, and privacy level. + * + * @param returnType the return type of the method + * @param name the name of the method + * @param privacy the privacy level */ public Method(JavaType returnType, String name, Privacy privacy) { init(returnType, name); @@ -76,14 +70,11 @@ public Method(JavaType returnType, String name, Privacy privacy) { } /** - * Generate a method of return type returnType with the chosen modifier - * - * @param returnType - * the return of the method - * @param name - * the name for the method - * @param modifier - * the modifier for the method + * Generates a method with the specified return type, name, and modifier. + * + * @param returnType the return type of the method + * @param name the name of the method + * @param modifier the modifier for the method */ public Method(JavaType returnType, String name, Modifier modifier) { init(returnType, name); @@ -91,16 +82,12 @@ public Method(JavaType returnType, String name, Modifier modifier) { } /** - * Generate a method of return type returnType with the chosen privacy and modifier - * - * @param returnType - * the return of the method - * @param name - * the name for the method - * @param privacy - * the privacy level - * @param modifier - * the modifier for the method + * Generates a method with the specified return type, name, privacy, and modifier. + * + * @param returnType the return type of the method + * @param name the name of the method + * @param privacy the privacy level + * @param modifier the modifier for the method */ public Method(JavaType returnType, String name, Privacy privacy, Modifier modifier) { init(returnType, name); diff --git a/JavaGenerator/src/org/specs/generators/java/statements/IStatement.java b/JavaGenerator/src/org/specs/generators/java/statements/IStatement.java index d42b5ba1..1bedd2f5 100644 --- a/JavaGenerator/src/org/specs/generators/java/statements/IStatement.java +++ b/JavaGenerator/src/org/specs/generators/java/statements/IStatement.java @@ -1,20 +1,24 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.statements; import org.specs.generators.java.IGenerate; +/** + * Interface representing a Java statement for code generation. + * Extends {@link IGenerate} to provide code generation capabilities for statements. + */ public interface IStatement extends IGenerate { } diff --git a/JavaGenerator/src/org/specs/generators/java/types/JavaGenericType.java b/JavaGenerator/src/org/specs/generators/java/types/JavaGenericType.java index 4ecaf4b3..8b959c1f 100644 --- a/JavaGenerator/src/org/specs/generators/java/types/JavaGenericType.java +++ b/JavaGenerator/src/org/specs/generators/java/types/JavaGenericType.java @@ -1,14 +1,14 @@ /** * Copyright 2015 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.types; @@ -19,94 +19,129 @@ import tdrc.utils.StringUtils; +/** + * Represents a Java generic type for code generation, including the base type and any extending types. + */ public class JavaGenericType { - private JavaType theType; - private List extendingTypes; - - public JavaGenericType(JavaType theType) { - setTheType(theType); - extendingTypes = new UniqueList<>(); - } - - public boolean addType(JavaType extendedType) { - return extendingTypes.add(extendedType); - } - - public JavaType getTheType() { - return theType; - } - - public void setTheType(JavaType theType) { - this.theType = theType; - } - - public List getExtendingTypes() { - return extendingTypes; - } - - public void setExtendingTypes(List extendingTypes) { - this.extendingTypes = extendingTypes; - } - - @Override - public String toString() { - final String toString = "<" + getCanonicalType() + ">"; - return toString; - } - - /** - * Return a simple representation of this types, i.e., do not include - * package. Does not include the '<' and '>' delimiters - * - * @return - */ - public String getSimpleType() { - String toString = theType.getSimpleType(); - if (!extendingTypes.isEmpty()) { - toString += " extends "; - toString += StringUtils.join(extendingTypes, JavaType::getSimpleType, "& "); - } - return toString; - } - - /** - * Return the canonical representation of this types, i.e., include package. - * Does not include the '<' and '>' delimiters - * - * @return - */ - public String getCanonicalType() { - String toString = theType.getCanonicalName(); - if (!extendingTypes.isEmpty()) { - toString += " extends "; - toString += StringUtils.join(extendingTypes, JavaType::getCanonicalName, "& "); - } - return toString; - } - - /** - * Return a simple representation of this types, i.e., do not include - * package, including the '<' and '>' delimiters - * - * @return - */ - public String getWrappedSimpleType() { - String toString = "<" + theType.getSimpleType(); - - if (!extendingTypes.isEmpty()) { - toString += " extends "; - toString += StringUtils.join(extendingTypes, JavaType::getSimpleType, "& "); - } - toString += ">"; - return toString; - } - - @Override - public JavaGenericType clone() { - - final JavaGenericType genericType = new JavaGenericType(theType.clone()); - genericType.extendingTypes.forEach(ext -> genericType.addType(ext.clone())); - return genericType; - } + private JavaType theType; + private List extendingTypes; + + /** + * Constructs a JavaGenericType with the specified base type. + * + * @param theType the base {@link JavaType} + */ + public JavaGenericType(JavaType theType) { + setTheType(theType); + extendingTypes = new UniqueList<>(); + } + + /** + * Adds an extending type to this generic type. + * + * @param extendedType the {@link JavaType} to add + * @return true if the type was added, false if it was already present + */ + public boolean addType(JavaType extendedType) { + return extendingTypes.add(extendedType); + } + + /** + * Returns the base type of this generic type. + * + * @return the base {@link JavaType} + */ + public JavaType getTheType() { + return theType; + } + + /** + * Sets the base type of this generic type. + * + * @param theType the base {@link JavaType} + */ + public void setTheType(JavaType theType) { + this.theType = theType; + } + + /** + * Returns the list of extending types. + * + * @return a list of {@link JavaType} + */ + public List getExtendingTypes() { + return extendingTypes; + } + + /** + * Sets the list of extending types. + * + * @param extendingTypes the list of {@link JavaType} + */ + public void setExtendingTypes(List extendingTypes) { + this.extendingTypes = extendingTypes; + } + + /** + * Returns the string representation of this generic type, including canonical type information. + * + * @return the string representation + */ + @Override + public String toString() { + final String toString = "<" + getCanonicalType() + ">"; + return toString; + } + + /** + * Returns a simple representation of this type, excluding package names and angle brackets. + * + * @return the simple type string + */ + public String getSimpleType() { + String toString = theType.getSimpleType(); + if (!extendingTypes.isEmpty()) { + toString += " extends "; + toString += StringUtils.join(extendingTypes, JavaType::getSimpleType, "& "); + } + return toString; + } + + /** + * Returns the canonical representation of this type, including package names but excluding angle brackets. + * + * @return the canonical type string + */ + public String getCanonicalType() { + String toString = theType.getCanonicalName(); + if (!extendingTypes.isEmpty()) { + toString += " extends "; + toString += StringUtils.join(extendingTypes, JavaType::getCanonicalName, "& "); + } + return toString; + } + + /** + * Returns a simple representation of this type, including angle brackets. + * + * @return the wrapped simple type string + */ + public String getWrappedSimpleType() { + String toString = "<" + theType.getSimpleType(); + + if (!extendingTypes.isEmpty()) { + toString += " extends "; + toString += StringUtils.join(extendingTypes, JavaType::getSimpleType, "& "); + } + toString += ">"; + return toString; + } + + @Override + public JavaGenericType clone() { + final JavaGenericType genericType = new JavaGenericType(theType.clone()); + genericType.extendingTypes.forEach(ext -> genericType.addType(ext.clone())); + return genericType; + } } diff --git a/JavaGenerator/src/org/specs/generators/java/types/JavaType.java b/JavaGenerator/src/org/specs/generators/java/types/JavaType.java index faea3bff..8d90468e 100644 --- a/JavaGenerator/src/org/specs/generators/java/types/JavaType.java +++ b/JavaGenerator/src/org/specs/generators/java/types/JavaType.java @@ -8,7 +8,7 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.types; @@ -19,6 +19,9 @@ import tdrc.utils.Pair; import tdrc.utils.StringUtils; +/** + * Represents a Java type for code generation, including name, package, array information, and generics. + */ public class JavaType { private String name; @@ -30,21 +33,20 @@ public class JavaType { private List generics; /** - * A JavaType with name and package + * Constructs a JavaType with the specified name, package, and array dimension. * - * @param name - * @param _package - * @param arrayDimension - * the dimension of the array (≤ 0 means that this javatype is not an array + * @param name the type name + * @param _package the package name + * @param arrayDimension the array dimension (≤ 0 means not an array) */ public JavaType(String name, String _package, int arrayDimension) { init(name, _package, arrayDimension); } /** - * Instance of a JavaType based on a loaded class + * Constructs a JavaType based on a loaded class. * - * @param thisClass + * @param thisClass the {@link Class} to base the type on */ public JavaType(Class thisClass) { this(thisClass.getSimpleName(), thisClass.getPackage().getName(), thisClass.isArray()); @@ -52,36 +54,42 @@ public JavaType(Class thisClass) { } /** - * A JavaType with name, package and if it is an array (uses dimension of 1 by default) + * Constructs a JavaType with name, package, and array flag (dimension 1 if true). * - * @param name - * @param _package - * @param isArray - * is this javatype an array? + * @param name the type name + * @param _package the package name + * @param isArray true if this type is an array */ public JavaType(String name, String _package, boolean isArray) { this(name, _package, isArray ? 1 : 0); } /** - * Instance of a JavaType with the given name + * Constructs a JavaType with the given name. * - * @param name + * @param name the type name */ public JavaType(String name) { this(name, null, 0); } /** - * A JavaType with name and package + * Constructs a JavaType with name and package. * - * @param name - * @param _package + * @param name the type name + * @param _package the package name */ public JavaType(String name, String _package) { this(name, _package, 0); } + /** + * Creates a JavaType representing an enum. + * + * @param name the enum name + * @param _package the package name + * @return a new JavaType marked as enum + */ public static JavaType enumType(String name, String _package) { JavaType jt = new JavaType(name, _package); jt.setEnum(true); @@ -89,22 +97,20 @@ public static JavaType enumType(String name, String _package) { } /** - * A JavaType with name that is/isn't an array + * Constructs a JavaType with name and array flag. * - * @param name - * @param isArray - * is this javatype an array? + * @param name the type name + * @param isArray true if this type is an array */ public JavaType(String name, boolean isArray) { this(name, null, isArray); } /** - * A JavaType with name that is/isn't an array + * Constructs a JavaType with name and array dimension. * - * @param name - * @param arrayDimension - * the dimension of the array (≤ 0 means that this javatype is not an array + * @param name the type name + * @param arrayDimension the array dimension (≤ 0 means not an array) */ public JavaType(String name, int arrayDimension) { this(name, null, arrayDimension); @@ -148,45 +154,45 @@ private void init(String name, String _package, int arrayDimension) { } /** - * Verify if this java type has a package defined + * Verify if this java type has a package defined. * - * @return + * @return true if the package is defined, false otherwise */ public boolean hasPackage() { return _package != null && !_package.isEmpty(); } /** - * Get the package of this java type + * Get the package of this java type. * - * @return + * @return the package name */ public String getPackage() { return _package; } /** - * Set the package of this java type + * Set the package of this java type. * - * @param _package + * @param _package the package name */ public void setPackage(String _package) { this._package = _package; } /** - * Get the name of this java type + * Get the name of this java type. * - * @return + * @return the type name */ public String getName() { return name; } /** - * Get the complete name of this java type (package+name) + * Get the complete name of this java type (package+name). * - * @return + * @return the canonical name */ public String getCanonicalName() { if (hasPackage()) { @@ -197,9 +203,9 @@ public String getCanonicalName() { } /** - * Set the name of this java type + * Set the name of this java type. * - * @param name + * @param name the type name */ public void setName(String name) { this.name = name; @@ -207,52 +213,50 @@ public void setName(String name) { } /** - * See if this java type is a primitive + * See if this java type is a primitive. * - * @return + * @return true if the type is primitive, false otherwise */ public boolean isPrimitive() { return primitive; } /** - * @return the array + * @return true if this type is an array, false otherwise */ public boolean isArray() { return array; } /** - * Define if this is an array. This method updates the dimension size + * Define if this is an array. This method updates the dimension size. * - * @param array - * if true sets the arrayDimension to 1 else sets the arrayDimension to 0 + * @param array if true sets the arrayDimension to 1 else sets the arrayDimension to 0 */ public void setArray(boolean array) { this.array = array; - // this.arrayDimension = !array ? 0 : (arrayDimension < 1 ? 1 : - // arrayDimension); if (array) { - if (arrayDimension < 1) { arrayDimension = 1; } } else { - arrayDimension = 0; } } + /** + * Get the array dimension of this type. + * + * @return the array dimension + */ public int getArrayDimension() { return arrayDimension; } /** - * Sets the dimension of the array. This method updates the array field - * - * @param arrayDimension - * if arrayDimension > 0 then array is set to true; otherwise it is set to false + * Sets the dimension of the array. This method updates the array field. * + * @param arrayDimension if arrayDimension > 0 then array is set to true; otherwise it is set to false */ public void setArrayDimension(int arrayDimension) { this.arrayDimension = arrayDimension; @@ -262,41 +266,43 @@ public void setArrayDimension(int arrayDimension) { @Override public String toString() { String toString = (hasPackage() ? _package + "." : "") + name; - if (isArray()) { // conditions to avoid possible mistakes: && - // arrayDimension > 0 + if (isArray()) { toString += StringUtils.repeat("[]", arrayDimension); } return toString; } /** - * This method returns the simple representation of this type, i.e., does not include the package + * This method returns the simple representation of this type, i.e., does not include the package. * - * @return + * @return the simple type representation */ public String getSimpleType() { String toString = name + genericsToString(); - if (isArray()) { // conditions to avoid possible mistakes: && - // arrayDimension > 0 + if (isArray()) { toString += StringUtils.repeat("[]", arrayDimension); } return toString; } /** - * This method returns the canonical representation of this type, i.e., includes the package + * This method returns the canonical representation of this type, i.e., includes the package. * - * @return + * @return the canonical type representation */ public String getCanonicalType() { String toString = getCanonicalName() + genericsToCanonicalString(); - if (isArray()) {// conditions to avoid possible mistakes: && - // arrayDimension > 0 + if (isArray()) { toString += StringUtils.repeat("[]", arrayDimension); } return toString; } + /** + * Converts the generics of this type to a string representation. + * + * @return the generics string representation + */ public String genericsToString() { String genericStr = ""; if (!generics.isEmpty()) { @@ -307,6 +313,11 @@ public String genericsToString() { return genericStr; } + /** + * Converts the generics of this type to a canonical string representation. + * + * @return the generics canonical string representation + */ public String genericsToCanonicalString() { String genericStr = ""; if (!generics.isEmpty()) { @@ -318,7 +329,7 @@ public String genericsToCanonicalString() { } /** - * Says if this java type requires import or not + * Says if this java type requires import or not. * * @return true if import is required, false otherwise */ @@ -326,18 +337,40 @@ public boolean requiresImport() { return hasPackage() && !isPrimitive() && !_package.equals(JavaTypeFactory.JavaLangImport); } + /** + * Get the generics of this type. + * + * @return the list of generics + */ public List getGenerics() { return generics; } + /** + * Adds a generic type to this JavaType. + * + * @param genericType the {@link JavaGenericType} to add + * @return true if the generic was added, false otherwise + */ public boolean addGeneric(JavaGenericType genericType) { return generics.add(genericType); } + /** + * Adds a type as a generic to this JavaType. + * + * @param genericType the {@link JavaType} to add as a generic + * @return true if the generic was added, false otherwise + */ public boolean addTypeAsGeneric(JavaType genericType) { return addGeneric(new JavaGenericType(genericType)); } + /** + * Sets the generics of this type. + * + * @param generics the list of {@link JavaGenericType} to set + */ public void setGenerics(List generics) { this.generics = generics; } @@ -350,10 +383,20 @@ public JavaType clone() { return clone; } + /** + * Checks if this type is an enum. + * + * @return true if this type is an enum, false otherwise + */ public boolean isEnum() { return isEnum; } + /** + * Sets whether this type is an enum. + * + * @param isEnum true if this type is an enum, false otherwise + */ public void setEnum(boolean isEnum) { this.isEnum = isEnum; } diff --git a/JavaGenerator/src/org/specs/generators/java/types/JavaTypeFactory.java b/JavaGenerator/src/org/specs/generators/java/types/JavaTypeFactory.java index a395ec13..dbbd2d9c 100644 --- a/JavaGenerator/src/org/specs/generators/java/types/JavaTypeFactory.java +++ b/JavaGenerator/src/org/specs/generators/java/types/JavaTypeFactory.java @@ -1,14 +1,14 @@ /** * Copyright 2015 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.types; @@ -25,52 +25,113 @@ import tdrc.utils.Pair; import tdrc.utils.StringUtils; +/** + * Factory class for creating and manipulating {@link JavaType} and {@link JavaGenericType} instances for code generation. + */ public class JavaTypeFactory { static final String JavaLangImport = "java.lang"; + /** + * Returns a wildcard {@link JavaType} ("?"). + * + * @return a wildcard JavaType + */ public static final JavaType getWildCardType() { return new JavaType("?"); } + /** + * Returns a {@link JavaType} representing {@link Object}. + * + * @return a JavaType for Object + */ public static final JavaType getObjectType() { return new JavaType(Object.class); } + /** + * Returns a {@link JavaType} representing {@link String}. + * + * @return a JavaType for String + */ public static final JavaType getStringType() { return new JavaType(String.class); } + /** + * Returns a {@link JavaType} representing {@link Class}. + * + * @return a JavaType for Class + */ public static final JavaType getClassType() { return new JavaType(Class.class); } + /** + * Returns a {@link JavaType} representing the primitive boolean type. + * + * @return a JavaType for boolean + */ public static final JavaType getBooleanType() { return getPrimitiveType(BOOLEAN); } + /** + * Returns a {@link JavaType} representing the primitive int type. + * + * @return a JavaType for int + */ public static final JavaType getIntType() { return getPrimitiveType(INT); } + /** + * Returns a {@link JavaType} representing the primitive void type. + * + * @return a JavaType for void + */ public static final JavaType getVoidType() { return getPrimitiveType(VOID); } + /** + * Returns a {@link JavaType} representing the primitive double type. + * + * @return a JavaType for double + */ public static final JavaType getDoubleType() { return getPrimitiveType(DOUBLE); } + /** + * Returns a {@link JavaType} representing a generic List with the specified generic type. + * + * @param genericType the {@link JavaGenericType} for the List + * @return a JavaType for List + */ public static final JavaType getListJavaType(JavaGenericType genericType) { final JavaType listTType = new JavaType(List.class); listTType.addGeneric(genericType); return listTType; } + /** + * Returns a {@link JavaType} representing a generic List with the specified type. + * + * @param genericType the {@link JavaType} for the List + * @return a JavaType for List + */ public static final JavaType getListJavaType(JavaType genericType) { return getListJavaType(new JavaGenericType(genericType)); } + /** + * Returns a wildcard-extends {@link JavaGenericType} for the specified type. + * + * @param javaType the base type + * @return a wildcard-extends JavaGenericType + */ public static final JavaGenericType getWildExtendsType(JavaType javaType) { final JavaType wildType = getWildCardType(); final JavaGenericType wildExtendsType = new JavaGenericType(wildType); @@ -79,17 +140,20 @@ public static final JavaGenericType getWildExtendsType(JavaType javaType) { } /** - * Adds the generic javType to the given target javaType - * - * @param targetType - * target {@link JavaType} - * @param genericType - * {@link JavaType} to be converted to generic + * Adds a generic type to the given target type. + * + * @param targetType the target {@link JavaType} + * @param genericType the {@link JavaType} to convert to generic */ public static final void addGenericType(JavaType targetType, JavaType genericType) { targetType.addGeneric(new JavaGenericType(genericType)); } + /** + * Returns a {@link JavaType} representing a List of String. + * + * @return a JavaType for List + */ public static final JavaType getListStringJavaType() { final JavaType listStringType = new JavaType(List.class); final JavaGenericType genStrinType = new JavaGenericType(getStringType()); @@ -98,19 +162,16 @@ public static final JavaType getListStringJavaType() { } /** - * Get the JavaType representation of a given primitive type - * - * @param prim - * the {@link Primitive} to convert + * Get the {@link JavaType} representation of a given primitive type. + * + * @param prim the {@link Primitive} to convert * @return the JavaType of the given primitive */ public static final JavaType getPrimitiveType(Primitive prim) { - return new JavaType(prim.getType(), JavaTypeFactory.JavaLangImport); } public static final JavaType getPrimitiveWrapper(Primitive prim) { - return new JavaType(prim.getPrimitiveWrapper(), JavaTypeFactory.JavaLangImport); } @@ -123,17 +184,12 @@ public static final JavaType getPrimitiveWrapper(String primStr) { } public static final String getDefaultValue(JavaType type) { - if (type.isPrimitive()) { - if (type.getName().equals(Primitive.VOID.getType())) { - return ""; } - return type.getName().equals(Primitive.BOOLEAN.getType()) ? "false" : "0"; } - return "null"; } @@ -149,34 +205,32 @@ public static boolean isPrimitiveWrapper(String name) { } /** - * Converts the given {@link JavaClass} into a {@link JavaType} - * - * @param javaClass - * @return + * Converts the given {@link JavaClass} into a {@link JavaType}. + * + * @param javaClass the {@link JavaClass} to convert + * @return the converted {@link JavaType} */ public static JavaType convert(JavaClass javaClass) { - return new JavaType(javaClass.getName(), javaClass.getClassPackage()); } /** - * Converts the given {@link Class} into a {@link JavaType}
+ * Converts the given {@link Class} into a {@link JavaType}. * WARNING: Do not use this method to convert primitive types, it will throw an exception! If this is the * case, please use {@link JavaTypeFactory#getPrimitiveType(Primitive)} instead. - * - * @param javaClass - * @return + * + * @param javaClass the {@link Class} to convert + * @return the converted {@link JavaType} */ public static JavaType convert(Class javaClass) { - return new JavaType(javaClass); } /** - * Unwrap the primitive tipe. For instance, for an Integer type an int is returned - * - * @param attrClassType - * @return + * Unwraps the primitive type. For instance, for an Integer type an int is returned. + * + * @param simpleType the simple type name + * @return the unwrapped primitive type name */ public static String primitiveUnwrap(String simpleType) { if (!isPrimitiveWrapper(simpleType)) { @@ -189,6 +243,12 @@ public static String primitiveUnwrap(String simpleType) { return simpleType; } + /** + * Unwraps the primitive type. For instance, for an Integer type an int is returned. + * + * @param attrClassType the {@link JavaType} to unwrap + * @return the unwrapped {@link JavaType} + */ public static JavaType primitiveUnwrap(JavaType attrClassType) { String simpleType = attrClassType.getSimpleType(); if (!isPrimitiveWrapper(simpleType)) { @@ -202,11 +262,10 @@ public static JavaType primitiveUnwrap(JavaType attrClassType) { } /** - * This method process a string to find the array dimension. - * - * @param arrayDimString - * The input string, should only contain "[]" multiple times - * @return + * Processes a string to find the array dimension. + * + * @param type the input string, should only contain "[]" multiple times + * @return a {@link Pair} containing the type and its array dimension */ public static Pair splitTypeFromArrayDimension(String type) { final int arrayDimPos = type.indexOf("["); diff --git a/JavaGenerator/src/org/specs/generators/java/types/Primitive.java b/JavaGenerator/src/org/specs/generators/java/types/Primitive.java index ccfda036..440eb49d 100644 --- a/JavaGenerator/src/org/specs/generators/java/types/Primitive.java +++ b/JavaGenerator/src/org/specs/generators/java/types/Primitive.java @@ -1,14 +1,14 @@ /** * Copyright 2015 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.types; @@ -16,10 +16,9 @@ import tdrc.utils.StringUtils; /** - * Enumeration of the existing primitives in Java - * - * @author tiago + * Enumeration of the existing primitive types in Java for code generation. * + * @author tiago */ public enum Primitive { @@ -39,10 +38,22 @@ public enum Primitive { this.type = type; } + /** + * Returns the string representation of the primitive type. + * + * @return the type string + */ public String getType() { return type; } + /** + * Returns the {@link Primitive} corresponding to the given name. + * + * @param name the name of the primitive type + * @return the corresponding Primitive + * @throws RuntimeException if the type is not a primitive + */ public static Primitive getPrimitive(String name) { for (final Primitive primitive : values()) { @@ -54,6 +65,11 @@ public static Primitive getPrimitive(String name) { throw new RuntimeException("The type '" + name + "' is not a primitive."); } + /** + * Returns the wrapper class name for this primitive type. + * + * @return the wrapper class name + */ public String getPrimitiveWrapper() { if (equals(Primitive.INT)) { @@ -63,6 +79,12 @@ public String getPrimitiveWrapper() { } + /** + * Checks if the given name is a valid primitive type. + * + * @param name the name to check + * @return true if the name is a primitive type, false otherwise + */ public static boolean contains(String name) { for (final Primitive primitive : values()) { diff --git a/JavaGenerator/src/org/specs/generators/java/units/CompilationUnit.java b/JavaGenerator/src/org/specs/generators/java/units/CompilationUnit.java index caf3aa4f..e6320a79 100644 --- a/JavaGenerator/src/org/specs/generators/java/units/CompilationUnit.java +++ b/JavaGenerator/src/org/specs/generators/java/units/CompilationUnit.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.units; @@ -16,17 +16,23 @@ import org.specs.generators.java.IGenerate; /** - * The unit that contains the package, imports, main class and possibly subclasses - * - * @author Tiago + * Represents a Java compilation unit, containing package, imports, main class, and possibly subclasses. * + *

This class implements {@link IGenerate} to provide code generation for a Java compilation unit structure.

+ * + * @author Tiago */ public class CompilationUnit implements IGenerate { + /** + * Generates code for the compilation unit with the given indentation. + * + * @param indentation the indentation level + * @return a {@link StringBuilder} containing the generated code + */ @Override public StringBuilder generateCode(int indentation) { - - return null; + return null; } } diff --git a/JavaGenerator/src/org/specs/generators/java/utils/UniqueList.java b/JavaGenerator/src/org/specs/generators/java/utils/UniqueList.java index c8b3ff6e..78687a5e 100644 --- a/JavaGenerator/src/org/specs/generators/java/utils/UniqueList.java +++ b/JavaGenerator/src/org/specs/generators/java/utils/UniqueList.java @@ -1,58 +1,88 @@ /* * Copyright 2013 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.utils; import java.util.ArrayList; import java.util.Collection; +/** + * A list implementation that only allows unique elements. Extends {@link ArrayList} and overrides add methods to prevent duplicates. + * + * @param the type of elements in this list + */ public class UniqueList extends ArrayList { - /** - * Auto-Generated serial - */ - private static final long serialVersionUID = 8776711618197815102L; + /** + * Auto-Generated serial version UID. + */ + private static final long serialVersionUID = 8776711618197815102L; - @Override - public boolean add(E arg0) { - if (!contains(arg0)) { - return super.add(arg0); - } - return false; - } + /** + * Adds the specified element to the list if it is not already present. + * + * @param arg0 the element to be added + * @return true if the element was added, false otherwise + */ + @Override + public boolean add(E arg0) { + if (!contains(arg0)) { + return super.add(arg0); + } + return false; + } - @Override - public void add(int index, E element) { - if (!contains(element)) { - super.add(index, element); - } - } + /** + * Inserts the specified element at the specified position in the list if it is not already present. + * + * @param index index at which the specified element is to be inserted + * @param element element to be inserted + */ + @Override + public void add(int index, E element) { + if (!contains(element)) { + super.add(index, element); + } + } - @Override - public boolean addAll(Collection c) { - for (final E element : c) { - add(element); - } - return true; - } + /** + * Adds all of the elements in the specified collection to the list if they are not already present. + * + * @param c collection containing elements to be added + * @return true if the list changed as a result of the call + */ + @Override + public boolean addAll(Collection c) { + for (final E element : c) { + add(element); + } + return true; + } - @Override - public boolean addAll(int index, Collection c) { - for (final E element : c) { - if (!contains(element)) { - add(index, element); - index++; - } - } - return true; - } + /** + * Inserts all of the elements in the specified collection into the list at the specified position, if not already present. + * + * @param index index at which to insert the first element from the specified collection + * @param c collection containing elements to be added + * @return true if the list changed as a result of the call + */ + @Override + public boolean addAll(int index, Collection c) { + for (final E element : c) { + if (!contains(element)) { + add(index, element); + index++; + } + } + return true; + } } diff --git a/JavaGenerator/src/org/specs/generators/java/utils/Utils.java b/JavaGenerator/src/org/specs/generators/java/utils/Utils.java index a8c5567b..cd5c6858 100644 --- a/JavaGenerator/src/org/specs/generators/java/utils/Utils.java +++ b/JavaGenerator/src/org/specs/generators/java/utils/Utils.java @@ -8,7 +8,7 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.specs.generators.java.utils; @@ -18,14 +18,15 @@ import java.io.File; +/** + * Utility class for Java code generation tasks, such as indentation, file output, and string manipulation. + */ public class Utils { private static final String INDENTER = " "; - // private static final String INDENTER = "\t"; - /** - * Returns a {@link StringBuilder} containing the desired indentation + * Returns a {@link StringBuilder} containing the desired indentation. * * @param indentation the level of indentation * @return {@link StringBuilder} with indentation @@ -39,13 +40,13 @@ public static StringBuilder indent(int indentation) { } /** - * Generates the java class/enum/interface into the requested folder, according to the class' package + * Generates the Java class/enum/interface into the requested folder, according to the class' package. * - * @param outputDir - * @param java the class to generate and write in the output folder - * @param replace replace existing file? + * @param outputDir the output directory + * @param java the class to generate and write in the output folder + * @param replace whether to replace existing file + * @return true if the file was written or replaced, false otherwise */ - public static boolean generateToFile(File outputDir, ClassType java, boolean replace) { final String pack = java.getClassPackage(); final String name = java.getName(); @@ -54,11 +55,11 @@ public static boolean generateToFile(File outputDir, ClassType java, boolean rep } /** - * Create the file path according to the package of the class/interface + * Creates the file path according to the package of the class/interface. * * @param outputDir the output directory - * @param pack the class/interface package - * @param name the class/interface name + * @param pack the class/interface package + * @param name the class/interface name * @return {@link File} containing the new file path */ private static File getFilePath(File outputDir, String pack, String name) { @@ -75,11 +76,12 @@ private static File getFilePath(File outputDir, String pack, String name) { } /** - * Write the java code in an output file + * Writes the Java code to an output file. * - * @param outputFile the file destiny of the code - * @param java the code to generate and write; - * @param replace replace existing file? + * @param outputFile the file destination of the code + * @param java the code to generate and write + * @param replace whether to replace existing file + * @return true if the file was written or replaced, false otherwise */ private static boolean writeToFile(File outputFile, IGenerate java, boolean replace) { final StringBuilder generatedJava = java.generateCode(0); @@ -90,19 +92,33 @@ private static boolean writeToFile(File outputFile, IGenerate java, boolean repl return false; } + /** + * Creates the directory if it does not exist. + * + * @param dir the directory to create + */ public static void makeDirs(File dir) { if (!dir.exists()) { dir.mkdirs(); } } + /** + * Capitalizes the first character of the given string. + * + * @param string the input string + * @return the string with the first character in uppercase + */ public static String firstCharToUpper(String string) { return string.substring(0, 1).toUpperCase() + string.substring(1); } + /** + * Returns the newline character. + * + * @return the newline character + */ public static String ln() { return "\n"; - //return SpecsIo.getNewline(); } - } diff --git a/JsEngine/README.md b/JsEngine/README.md new file mode 100644 index 00000000..a90abf2f --- /dev/null +++ b/JsEngine/README.md @@ -0,0 +1,14 @@ +# JsEngine + +JsEngine is a Java library for integrating and executing JavaScript code using various engines, including GraalVM and Node.js. It provides abstractions for engine selection, file type handling, and JavaScript code execution from Java applications. + +## Features +- JavaScript engine abstraction (GraalVM, Node.js, etc.) +- File type and resource management +- Utilities for JavaScript code execution and integration + +## Usage +Add JsEngine to your Java project to run and interact with JavaScript code from Java. + +## License +This project is licensed under the Apache License 2.0. diff --git a/JsEngine/src/com/oracle/truffle/polyglot/SpecsPolyglot.java b/JsEngine/src/com/oracle/truffle/polyglot/SpecsPolyglot.java index 7194d2a7..1b7b9db2 100644 --- a/JsEngine/src/com/oracle/truffle/polyglot/SpecsPolyglot.java +++ b/JsEngine/src/com/oracle/truffle/polyglot/SpecsPolyglot.java @@ -3,8 +3,18 @@ import com.oracle.truffle.js.runtime.GraalJSException; import com.oracle.truffle.js.runtime.builtins.JSErrorObject; +/** + * Utility class for working with the Truffle Polyglot API in GraalVM. + * Provides methods for polyglot context management and language interoperability. + */ public class SpecsPolyglot { + /** + * Retrieves the GraalJSException from a possible error object. + * + * @param possibleError The object that might represent an error. + * @return The GraalJSException if the object is a valid error, otherwise null. + */ public static GraalJSException getException(Object possibleError) { if (!(possibleError instanceof PolyglotWrapper)) { return null; @@ -17,8 +27,7 @@ public static GraalJSException getException(Object possibleError) { if (!(guestObject instanceof JSErrorObject)) { return null; } - // System.out.println("GUEST: " + ((JSErrorObject) guestObject).ownPropertyKeys()); - // System.out.println("STACK: " + ((JSError) ((JSErrorObject) guestObject).get("stack")).); + return ((JSErrorObject) guestObject).getException(); } } diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/AJsEngine.java b/JsEngine/src/pt/up/fe/specs/jsengine/AJsEngine.java index b4e07267..5bca1ba5 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/AJsEngine.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/AJsEngine.java @@ -8,7 +8,7 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.jsengine; @@ -22,10 +22,18 @@ import pt.up.fe.specs.util.classmap.FunctionClassMap; +/** + * Abstract base class for JavaScript engine implementations. + * Defines the contract for engine execution and resource management. + */ public abstract class AJsEngine implements JsEngine { private final FunctionClassMap toJsRules; + /** + * Constructor for AJsEngine. + * Initializes the rules for converting Java objects to JavaScript objects. + */ public AJsEngine() { this.toJsRules = new FunctionClassMap<>(); @@ -36,30 +44,33 @@ public AJsEngine() { } /** - * If a List, apply adapt over all elements of the list and convert to array. + * Converts a List to a JavaScript array. + * Applies the conversion rule to each element of the list. * - * @param list - * @return + * @param list the List to be converted + * @return the JavaScript array representation of the List */ private Object listToJs(List list) { return toNativeArray(list.stream().map(this::toJs).toArray()); } /** - * If a Set, apply adapt over all elements of the Set and convert to array. + * Converts a Set to a JavaScript array. + * Applies the conversion rule to each element of the Set. * - * @param set - * @return + * @param set the Set to be converted + * @return the JavaScript array representation of the Set */ private Object setToJs(Set set) { return toNativeArray(set.stream().map(this::toJs).toArray()); } /** - * If a JsonArray, convert to List and call toJs() again. + * Converts a JsonArray to a JavaScript object. + * Converts the JsonArray to a List and applies the conversion rule. * - * @param jsonArray - * @return + * @param jsonArray the JsonArray to be converted + * @return the JavaScript object representation of the JsonArray */ private Object jsonArrayToJs(JsonArray jsonArray) { var list = new ArrayList(); @@ -69,11 +80,24 @@ private Object jsonArrayToJs(JsonArray jsonArray) { return toJs(list); } + /** + * Adds a custom rule for converting Java objects to JavaScript objects. + * + * @param key the class type of the Java object + * @param rule the conversion function + * @param the type of the Java object + * @param the type of the key, which extends VS + */ @Override public void addToJsRule(Class key, Function rule) { toJsRules.put(key, rule); } + /** + * Retrieves the rules for converting Java objects to JavaScript objects. + * + * @return the map of conversion rules + */ @Override public FunctionClassMap getToJsRules() { return toJsRules; diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/ForOfType.java b/JsEngine/src/pt/up/fe/specs/jsengine/ForOfType.java index 13fa3199..af1ccdf9 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/ForOfType.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/ForOfType.java @@ -13,10 +13,18 @@ package pt.up.fe.specs.jsengine; +/** + * Enum representing the types of iteration supported by JavaScript 'for...of' loops. + */ public enum ForOfType { - // Natively supports 'for(var a of...) + /** + * Represents native support for 'for(var a of...)' loops. + */ NATIVE, - // Support the code 'for each (...' as for-each implementation + + /** + * Represents support for 'for each (...' as a for-each implementation. + */ FOR_EACH; } diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/JsEngine.java b/JsEngine/src/pt/up/fe/specs/jsengine/JsEngine.java index 2bb31625..102d6f90 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/JsEngine.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/JsEngine.java @@ -24,6 +24,7 @@ import pt.up.fe.specs.util.exceptions.NotImplementedException; /** + * Main class for JavaScript engine integration and execution. * Represents the JavaScript engine used by LARA. * * TODO: Replace 'Bindings' with 'Object'. Only JsEngine should manipulate JS objects @@ -33,96 +34,117 @@ */ public interface JsEngine { - // ScriptEngine getEngine(); - + /** + * Retrieves the type of "for-of" loop supported by the engine. + * + * @return the type of "for-of" loop + */ ForOfType getForOfType(); /** * Based on this site: http://programmaticallyspeaking.com/nashorns-jsobject-in-context.html * - * @return + * @return the undefined object representation */ Object getUndefined(); + /** + * Checks if the given object is undefined. + * + * @param object the object to check + * @return true if the object is undefined, false otherwise + */ boolean isUndefined(Object object); + /** + * Converts the given object to its string representation. + * + * @param object the object to stringify + * @return the string representation of the object + */ String stringify(Object object); /// ENGINE FEATURES /** + * Checks if the engine supports automatic property transformation. * - * @return if true, the engine can automatically transform obj.prop to obj.getProp(). False otherwise + * @return true if the engine supports properties, false otherwise */ boolean supportsProperties(); /// TYPE CONVERSIONS - // Bindings asBindings(Object value); - + /** + * Converts the given value to a boolean. + * + * @param value the value to convert + * @return the boolean representation of the value + */ boolean asBoolean(Object value); + /** + * Converts the given value to a double. + * + * @param value the value to convert + * @return the double representation of the value + */ double asDouble(Object value); /** - * Attempts to convert a JS bindings value to a Java object. + * Attempts to convert a JavaScript bindings value to a Java object. * - * @param value - * @return + * @param value the value to convert + * @return the Java object representation of the value */ Object toJava(Object value); /** + * Retrieves the bindings of the engine scope. * - * @return the Bindings of the engine scope + * @return the bindings object */ Object getBindings(); /** * Creates a new JavaScript array. - * * - * @return a + * @return a new JavaScript array */ Object newNativeArray(); /** * Creates a new JavaScript map. - * * - * @return a + * @return a new JavaScript map */ Object newNativeMap(); /** - * Converts an array of objects to a JavaScript array - * - * @param values - * the array of values - * @return a javascript array containing all the elements in values, with the same indexes + * Converts an array of objects to a JavaScript array. + * + * @param values the array of values + * @return a JavaScript array containing all the elements in values, with the same indexes */ Object toNativeArray(Object[] values); /** - * Converts a list of objects to a JavaScript array - * - * @param values - * the array of values - * @return a javascript array containing all the elements in values, with the same indexes + * Converts a collection of objects to a JavaScript array. + * + * @param values the collection of values + * @return a JavaScript array containing all the elements in values, with the same indexes */ default Object toNativeArray(Collection values) { return toNativeArray(values.toArray()); } /** - * Converts an array of ints to a JavaScript array - * - * @param values - * the array of values - * @return a javascript array containing all the elements in values, with the same indexes + * Converts an array of ints to a JavaScript array. + * + * @param values the array of values + * @return a JavaScript array containing all the elements in values, with the same indexes */ default Object toNativeArray(int[] values) { - Object[] newObject = new Object[values.length]; for (int i = 0; i < values.length; i++) { newObject[i] = values[i]; @@ -131,14 +153,12 @@ default Object toNativeArray(int[] values) { } /** - * Converts an array of longs to a JavaScript array - * - * @param values - * the array of values - * @return a javascript array containing all the elements in values, with the same indexes + * Converts an array of longs to a JavaScript array. + * + * @param values the array of values + * @return a JavaScript array containing all the elements in values, with the same indexes */ default Object toNativeArray(long[] values) { - Object[] newObject = new Object[values.length]; for (int i = 0; i < values.length; i++) { newObject[i] = values[i]; @@ -147,14 +167,12 @@ default Object toNativeArray(long[] values) { } /** - * Converts an array of floats to a JavaScript array - * - * @param values - * the array of values - * @return a javascript array containing all the elements in values, with the same indexes + * Converts an array of floats to a JavaScript array. + * + * @param values the array of values + * @return a JavaScript array containing all the elements in values, with the same indexes */ default Object toNativeArray(float[] values) { - Object[] newObject = new Object[values.length]; for (int i = 0; i < values.length; i++) { newObject[i] = values[i]; @@ -163,14 +181,12 @@ default Object toNativeArray(float[] values) { } /** - * Converts an array of doubles to a JavaScript array - * - * @param values - * the array of values - * @return a javascript array containing all the elements in values, with the same indexes + * Converts an array of doubles to a JavaScript array. + * + * @param values the array of values + * @return a JavaScript array containing all the elements in values, with the same indexes */ default Object toNativeArray(double[] values) { - Object[] newObject = new Object[values.length]; for (int i = 0; i < values.length; i++) { newObject[i] = values[i]; @@ -179,14 +195,12 @@ default Object toNativeArray(double[] values) { } /** - * Converts an array of booleans to a JavaScript array - * - * @param values - * the array of values - * @return a javascript array containing all the elements in values, with the same indexes + * Converts an array of booleans to a JavaScript array. + * + * @param values the array of values + * @return a JavaScript array containing all the elements in values, with the same indexes */ default Object toNativeArray(boolean[] values) { - Object[] newObject = new Object[values.length]; for (int i = 0; i < values.length; i++) { newObject[i] = values[i]; @@ -195,14 +209,12 @@ default Object toNativeArray(boolean[] values) { } /** - * Converts an array of chars to a JavaScript array - * - * @param values - * the array of values - * @return a javascript array containing all the elements in values, with the same indexes + * Converts an array of chars to a JavaScript array. + * + * @param values the array of values + * @return a JavaScript array containing all the elements in values, with the same indexes */ default Object toNativeArray(char[] values) { - Object[] newObject = new Object[values.length]; for (int i = 0; i < values.length; i++) { newObject[i] = values[i]; @@ -211,14 +223,12 @@ default Object toNativeArray(char[] values) { } /** - * Converts an array of bytes to a JavaScript array - * - * @param values - * the array of values - * @return a javascript array containing all the elements in values, with the same indexes + * Converts an array of bytes to a JavaScript array. + * + * @param values the array of values + * @return a JavaScript array containing all the elements in values, with the same indexes */ default Object toNativeArray(byte[] values) { - Object[] newObject = new Object[values.length]; for (int i = 0; i < values.length; i++) { newObject[i] = values[i]; @@ -227,14 +237,12 @@ default Object toNativeArray(byte[] values) { } /** - * Converts an array of shorts to a JavaScript array - * - * @param values - * the array of values - * @return a javascript array containing all the elements in values, with the same indexes + * Converts an array of shorts to a JavaScript array. + * + * @param values the array of values + * @return a JavaScript array containing all the elements in values, with the same indexes */ default Object toNativeArray(short[] values) { - Object[] newObject = new Object[values.length]; for (int i = 0; i < values.length; i++) { newObject[i] = values[i]; @@ -246,125 +254,150 @@ default Object toNativeArray(short[] values) { * Evaluates the given string of JavaScript code. It is preferable to use the version that accepts a string with a * description of the source. * - * @param code - * @return + * @param code the JavaScript code to evaluate + * @return the result of the evaluation */ default Object eval(String code) { return eval(code, "unnamed_js_code"); } /** + * Evaluates the given string of JavaScript code with a specified source description. * - * @param code - * @param source - * a String identifying the source - * @return + * @param code the JavaScript code to evaluate + * @param source a string identifying the source + * @return the result of the evaluation */ Object eval(String code, String source); /** + * Evaluates the given script with a specified scope, type, and source description. * - * @param script - * @param scope - * @param type - * @param source - * a String identifying the source. If the code is loaded as module and this function has been called - * before with the same value for source, it might consider the module is already in cache - * @return + * @param script the JavaScript script to evaluate + * @param scope the scope in which to evaluate the script + * @param type the type of the script + * @param source a string identifying the source + * @return the result of the evaluation */ Object eval(String script, Object scope, JsFileType type, String source); + /** + * Evaluates the given string of JavaScript code with a specified type and source description. + * + * @param code the JavaScript code to evaluate + * @param type the type of the script + * @param source a string identifying the source + * @return the result of the evaluation + */ default Object eval(String code, JsFileType type, String source) { throw new NotImplementedException(this); } + /** + * Evaluates the given JavaScript file. + * + * @param jsFile the JavaScript file to evaluate + * @return the result of the evaluation + */ default Object evalFile(File jsFile) { return evalFile(jsFile, JsFileType.NORMAL); } + /** + * Evaluates the given JavaScript file with a specified type. + * + * @param jsFile the JavaScript file to evaluate + * @param type the type of the script + * @return the result of the evaluation + */ default Object evalFile(File jsFile, JsFileType type) { return evalFile(jsFile, type, null); } /** + * Evaluates the given JavaScript file with a specified type and content. * - * @param jsFile - * @param type - * @param content - * if the contents of the file need to be changed, but you need to load as a file, so that the relative - * paths in imports keep working - * @return + * @param jsFile the JavaScript file to evaluate + * @param type the type of the script + * @param content the content of the file + * @return the result of the evaluation */ default Object evalFile(File jsFile, JsFileType type, String content) { throw new NotImplementedException(this); } + /** + * Calls the given function with the specified arguments. + * + * @param function the function to call + * @param args the arguments to pass to the function + * @return the result of the function call + */ Object call(Object function, Object... args); - // default Bindings createBindings() { - // return getEngine().createBindings(); - // } - /** + * Checks if the given object is an array. * - * @param object - * @return true if the given object is an array, false otherwise + * @param object the object to check + * @return true if the object is an array, false otherwise */ boolean isArray(Object object); /** + * Checks if the given object is a number. * - * @param object - * @return true if the given object is a number, false otherwise + * @param object the object to check + * @return true if the object is a number, false otherwise */ boolean isNumber(Object object); /** + * Checks if the given object has members. * - * @param object - * @return true if the given object has members, false otherwise + * @param object the object to check + * @return true if the object has members, false otherwise */ boolean isObject(Object object); /** + * Checks if the given object is a string. * - * @param object - * @return true if the given object is a string, false otherwise + * @param object the object to check + * @return true if the object is a string, false otherwise */ boolean isString(Object object); /** + * Checks if the given object is a boolean. * - * @param object - * @return true if the given object is a boolean, false otherwise + * @param object the object to check + * @return true if the object is a boolean, false otherwise */ boolean isBoolean(Object object); /** + * Checks if the given object can be called (executed). * - * @param object - * @return true if the object can be called (executed) + * @param object the object to check + * @return true if the object can be called, false otherwise */ boolean isFunction(Object object); - // Object put(Bindings var, String member, Object value); - - // public Object remove(Bindings object, Object key); - /** + * Retrieves the values inside the given object (e.g., map, array). * - * @param object - * @return the value inside the given object (e.g., map, array) + * @param object the object to retrieve values from + * @return a collection of values inside the object */ Collection getValues(Object object); /** * Converts an object to the given Java class. * - * @param - * @param object - * @param toConvert - * @return + * @param the target class type + * @param object the object to convert + * @param targetClass the target class + * @return the converted object */ T convert(Object object, Class targetClass); @@ -373,53 +406,105 @@ default Object evalFile(File jsFile, JsFileType type, String content) { /** * Sets the specified value with the specified key in the ENGINE_SCOPE Bindings of the protected context field. * - * @param key - * @param value + * @param key the key to set + * @param value the value to set */ void put(String key, Object value); /// Bindings-like operations + /** + * Sets the specified value with the specified key in the given bindings object. + * + * @param bindings the bindings object + * @param key the key to set + * @param value the value to set + * @return the previous value associated with the key, or null if there was no mapping for the key + */ Object put(Object bindings, String key, Object value); + /** + * Removes the specified key from the given bindings object. + * + * @param bindings the bindings object + * @param key the key to remove + * @return the value associated with the key, or null if there was no mapping for the key + */ Object remove(Object bindings, String key); + /** + * Retrieves the set of keys in the given bindings object. + * + * @param bindings the bindings object + * @return the set of keys in the bindings object + */ Set keySet(Object bindings); + /** + * Retrieves the value associated with the specified key in the given bindings object. + * + * @param bindings the bindings object + * @param key the key to retrieve + * @return the value associated with the key, or null if there was no mapping for the key + */ Object get(Object bindings, String key); + /** + * Retrieves the value associated with the specified key in the given bindings object and converts it to the target class. + * + * @param the target class type + * @param bindings the bindings object + * @param key the key to retrieve + * @param targetClass the target class + * @return the converted value associated with the key + */ default T get(Object bindings, String key, Class targetClass) { return convert(get(bindings, key), targetClass); } + /** + * Retrieves the value associated with the specified key in the engine scope bindings. + * + * @param key the key to retrieve + * @return the value associated with the key, or null if there was no mapping for the key + */ default Object get(String key) { return get(getBindings(), key); } + /** + * Retrieves the value associated with the specified key in the engine scope bindings and converts it to the target class. + * + * @param the target class type + * @param key the key to retrieve + * @param targetClass the target class + * @return the converted value associated with the key + */ default T get(String key, Class targetClass) { return get(getBindings(), key, targetClass); } /** - * Adds a JS conversion rule for objects that are instances of a given class. + * Adds a JavaScript conversion rule for objects that are instances of a given class. * - * @param key - * @param rule + * @param the base class type + * @param the specific class type + * @param key the class to add the rule for + * @param rule the conversion rule */ - // void addToJsRule(Class key, BiFunction rule); void addToJsRule(Class key, Function rule); /** - * Maps classes to JS conversion rules. + * Maps classes to JavaScript conversion rules. * - * @return + * @return the function class map containing the conversion rules */ FunctionClassMap getToJsRules(); /** * Converts a given Java object to a more compatible type in JavaScript. * - * New conversion rules can be added with the method + * New conversion rules can be added with the method {@link #addToJsRule(Class, Function)}. * * Conversions currently supported by default:
* - null to undefined;
@@ -428,11 +513,10 @@ default T get(String key, Class targetClass) { * - Java Set to JS array;
* - JsonArray to JS array;
* - * @param javaObject - * @return + * @param javaObject the Java object to convert + * @return the converted JavaScript object */ default Object toJs(Object javaObject) { - // Null if (javaObject == null) { return getUndefined(); @@ -493,9 +577,10 @@ default Object toJs(Object javaObject) { } /** + * Retrieves a Throwable if the given object is an error generated by the engine. * - * @param possibleError - * @return a Throwable if the given object is an error generated by the engine. + * @param possibleError the object to check + * @return an Optional containing the Throwable if the object is an error, or an empty Optional otherwise */ default Optional getException(Object possibleError) { throw new NotImplementedException(this); diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/JsEngineLauncher.java b/JsEngine/src/pt/up/fe/specs/jsengine/JsEngineLauncher.java index 32d261fa..c6105702 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/JsEngineLauncher.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/JsEngineLauncher.java @@ -13,8 +13,16 @@ package pt.up.fe.specs.jsengine; +/** + * Utility class for launching JavaScript engines and managing their lifecycle. + */ public class JsEngineLauncher { + /** + * Main method for launching JavaScript engines and evaluating sample scripts. + * + * @param args Command-line arguments (not used). + */ public static void main(String[] args) { var engine = JsEngineType.GRAALVM_COMPAT.newEngine(); diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/JsEngineType.java b/JsEngine/src/pt/up/fe/specs/jsengine/JsEngineType.java index 2b6dccc3..aac9403b 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/JsEngineType.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/JsEngineType.java @@ -22,33 +22,59 @@ import pt.up.fe.specs.jsengine.graal.GraalvmJsEngine; import pt.up.fe.specs.util.exceptions.NotImplementedException; +/** + * Enum representing the types of JavaScript engines supported. + */ public enum JsEngineType { + /** + * Represents a GraalVM JavaScript engine with compatibility mode enabled. + */ GRAALVM_COMPAT, + + /** + * Represents a standard GraalVM JavaScript engine. + */ GRAALVM; /** - * Creates a new engine, according to the type. TODO: Move to JsEngineType - * - * @param type - * @param forbiddenClasses - * @return + * Creates a new JavaScript engine based on the specified type, forbidden classes, and working directory. + * + * @param type The type of JavaScript engine to create. + * @param forbiddenClasses A collection of classes that should be forbidden in the engine. + * @param engineWorkingDirectory The working directory for the engine. + * @return A new instance of the JavaScript engine. */ public JsEngine newEngine(JsEngineType type, Collection> forbiddenClasses, Path engineWorkingDirectory) { return newEngine(type, forbiddenClasses, engineWorkingDirectory, null, System.out); } + /** + * Creates a new JavaScript engine based on the specified type, forbidden classes, working directory, and node modules folder. + * + * @param type The type of JavaScript engine to create. + * @param forbiddenClasses A collection of classes that should be forbidden in the engine. + * @param engineWorkingDirectory The working directory for the engine. + * @param nodeModulesFolder The folder containing node modules, or null if not applicable. + * @return A new instance of the JavaScript engine. + */ public JsEngine newEngine(JsEngineType type, Collection> forbiddenClasses, Path engineWorkingDirectory, File nodeModulesFolder) { return newEngine(type, forbiddenClasses, engineWorkingDirectory, nodeModulesFolder, System.out); } + /** + * Creates a new JavaScript engine based on the specified type, forbidden classes, working directory, node modules folder, and output stream. + * + * @param type The type of JavaScript engine to create. + * @param forbiddenClasses A collection of classes that should be forbidden in the engine. + * @param engineWorkingDirectory The working directory for the engine. + * @param nodeModulesFolder The folder containing node modules, or null if not applicable. + * @param laraiOutputStream The output stream for the engine, or null if not applicable. + * @return A new instance of the JavaScript engine. + */ public JsEngine newEngine(JsEngineType type, Collection> forbiddenClasses, Path engineWorkingDirectory, File nodeModulesFolder, OutputStream laraiOutputStream) { - // System.out.println("TEST CLASSLOADER " + Test.class.getClassLoader()); - // System.out.println("JS ENGINE CLASS LOADER: " + GraalJSScriptEngine.class.getClassLoader()); - // System.out.println("THREAD CLASS LOADER: " + Thread.currentThread().getContextClassLoader()); - // Thread.currentThread().setContextClassLoader(GraalJSScriptEngine.class.getClassLoader()); switch (this) { case GRAALVM_COMPAT: return new GraalvmJsEngine(forbiddenClasses, true, engineWorkingDirectory, nodeModulesFolder, @@ -61,6 +87,11 @@ public JsEngine newEngine(JsEngineType type, Collection> forbiddenClass } } + /** + * Creates a new JavaScript engine with default settings. + * + * @return A new instance of the JavaScript engine. + */ public JsEngine newEngine() { return newEngine(this, Collections.emptyList(), null); } diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/JsEngineWebResources.java b/JsEngine/src/pt/up/fe/specs/jsengine/JsEngineWebResources.java index 4c19ed63..b4466101 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/JsEngineWebResources.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/JsEngineWebResources.java @@ -15,16 +15,31 @@ import pt.up.fe.specs.util.providers.WebResourceProvider; +/** + * Utility class for managing web resources for JavaScript engines. + */ public interface JsEngineWebResources { + /** + * Creates a new web resource provider for the given resource URL and version. + * + * @param resourceUrl the URL of the resource + * @param version the version of the resource + * @return a new instance of WebResourceProvider + */ static WebResourceProvider create(String resourceUrl, String version) { - return WebResourceProvider.newInstance("http://specs.fe.up.pt/resources/jsengine/", resourceUrl, version); + return WebResourceProvider.newInstance("https://specs.fe.up.pt/resources/jsengine/", resourceUrl, version); } - // Taken from https://unpkg.com/browse/@babel/standalone@7.15.6/ + /** + * Web resource provider for Babel JavaScript compiler. + * Taken from https://unpkg.com/browse/@babel/standalone@7.15.6/ + */ WebResourceProvider BABEL = create("babel.min.js", "v7.15.6"); - // WebResourceProvider BABEL_LATEST = WebResourceProvider.newInstance("https://unpkg.com/@babel/standalone/", - // "babel.js"); + + /** + * Web resource provider for Esprima JavaScript parser. + */ WebResourceProvider ESPRIMA = create("esprima.js", "v4.0.1"); } diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/JsFileType.java b/JsEngine/src/pt/up/fe/specs/jsengine/JsFileType.java index dd744438..efe7904d 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/JsFileType.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/JsFileType.java @@ -17,49 +17,62 @@ import pt.up.fe.specs.util.lazy.Lazy; import pt.up.fe.specs.util.providers.StringProvider; +/** + * Enum representing JavaScript file types (e.g., JS, MJS). + */ public enum JsFileType implements StringProvider { + /** + * Represents a standard JavaScript file with the ".js" extension. + */ NORMAL("js"), - MODULE("mjs"); /** - * CommonJS, supports features not available in strict mode (e.g. with) + * Represents a JavaScript module file with the ".mjs" extension. */ - // COMMON("cjs"); + MODULE("mjs"); private static final Lazy> HELPER = EnumHelperWithValue .newLazyHelperWithValue(JsFileType.class); private final String extension; + /** + * Constructor for JsFileType. + * + * @param extension the file extension associated with the JavaScript file type + */ private JsFileType(String extension) { this.extension = extension; } + /** + * Gets the file extension associated with the JavaScript file type. + * + * @return the file extension as a string + */ public String getExtension() { return extension; } + /** + * Gets the string representation of the file extension. + * + * @return the file extension as a string + */ @Override public String getString() { return extension; } + /** + * Retrieves the JsFileType based on the given file extension. + * + * @param extension the file extension to match + * @return the corresponding JsFileType + */ public static JsFileType getType(String extension) { return HELPER.get().fromValue(extension); - /* - switch (extension.toLowerCase()) { - case "js": - return NORMAL; - case "mjs": - return MODULE; - case "cjs": - return COMMON; - default: - throw new NotImplementedException(extension); - } - */ - } } diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/NodeJsEngine.java b/JsEngine/src/pt/up/fe/specs/jsengine/NodeJsEngine.java index 41894bc9..a44c1ce5 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/NodeJsEngine.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/NodeJsEngine.java @@ -27,245 +27,544 @@ import java.util.Set; import java.util.function.Function; +/** + * Implementation of a JavaScript engine using Node.js. + * Provides methods for executing JavaScript code in a Node.js process. + */ public class NodeJsEngine implements JsEngine { + /** + * Constructor for NodeJsEngine. + */ public NodeJsEngine() { // TODO Auto-generated constructor stub } + /** + * Adds a JavaScript rule to the engine. + * + * @param key the class key + * @param rule the function rule + * @param the value type + * @param the key type + */ @Override public void addToJsRule(Class key, Function rule) { throw new NotImplementedException(this); } + /** + * Converts the given value to a boolean. + * + * @param value the value to convert + * @return the boolean representation of the value + */ @Override public boolean asBoolean(Object value) { return (boolean) value; } + /** + * Converts the given value to a double. + * + * @param value the value to convert + * @return the double representation of the value + */ @Override public double asDouble(Object value) { return (double) value; } + /** + * Calls a JavaScript function with the given arguments. + * + * @param function the function to call + * @param args the arguments to pass to the function + * @return the result of the function call + */ @Override public Object call(Object function, Object... args) { throw new NotImplementedException(this); } + /** + * Converts an object to the specified target class. + * + * @param object the object to convert + * @param targetClass the target class + * @param the type of the target class + * @return the converted object + */ @Override public T convert(Object object, Class targetClass) { - // TODO: Implement throw new NotImplementedException(this); } + /** + * Evaluates JavaScript code. + * + * @param code the JavaScript code to evaluate + * @return the result of the evaluation + */ @Override public Object eval(String code) { - // TODO: Implement throw new NotImplementedException(this); } + /** + * Evaluates JavaScript code with the specified type and source. + * + * @param code the JavaScript code to evaluate + * @param type the type of the JavaScript file + * @param source the source of the JavaScript code + * @return the result of the evaluation + */ @Override public Object eval(String code, JsFileType type, String source) { throw new NotImplementedException(this); } + /** + * Evaluates JavaScript code with the specified scope, type, and source. + * + * @param script the JavaScript code to evaluate + * @param scope the scope for the evaluation + * @param type the type of the JavaScript file + * @param source the source of the JavaScript code + * @return the result of the evaluation + */ @Override public Object eval(String script, Object scope, JsFileType type, String source) { throw new NotImplementedException(this); } + /** + * Evaluates JavaScript code with the specified source. + * + * @param code the JavaScript code to evaluate + * @param source the source of the JavaScript code + * @return the result of the evaluation + */ @Override public Object eval(String code, String source) { throw new NotImplementedException(this); } + /** + * Evaluates a JavaScript file. + * + * @param jsFile the JavaScript file to evaluate + * @return the result of the evaluation + */ @Override public Object evalFile(File jsFile) { throw new NotImplementedException(this); } + /** + * Gets a value from the specified bindings using the given key. + * + * @param bindings the bindings to retrieve the value from + * @param key the key to use for retrieval + * @return the retrieved value + */ @Override public Object get(Object bindings, String key) { throw new NotImplementedException(this); } + /** + * Gets a value from the specified bindings using the given key and converts it to the target class. + * + * @param bindings the bindings to retrieve the value from + * @param key the key to use for retrieval + * @param targetClass the target class to convert the value to + * @param the type of the target class + * @return the retrieved and converted value + */ @Override public T get(Object bindings, String key, Class targetClass) { throw new NotImplementedException(this); } + /** + * Gets a value using the given key. + * + * @param key the key to use for retrieval + * @return the retrieved value + */ @Override public Object get(String key) { throw new NotImplementedException(this); } + /** + * Gets a value using the given key and converts it to the target class. + * + * @param key the key to use for retrieval + * @param targetClass the target class to convert the value to + * @param the type of the target class + * @return the retrieved and converted value + */ @Override public T get(String key, Class targetClass) { throw new NotImplementedException(this); } + /** + * Gets the bindings of the engine. + * + * @return the bindings + */ @Override public Object getBindings() { throw new NotImplementedException(this); } + /** + * Gets the exception from a possible error object. + * + * @param possibleError the possible error object + * @return an optional containing the exception if present + */ @Override public Optional getException(Object possibleError) { throw new NotImplementedException(this); } + /** + * Gets the type of "for-of" iteration supported by the engine. + * + * @return the "for-of" type + */ @Override public ForOfType getForOfType() { throw new NotImplementedException(this); } + /** + * Gets the rules for converting objects to JavaScript. + * + * @return the rules for conversion + */ @Override public FunctionClassMap getToJsRules() { throw new NotImplementedException(this); } + /** + * Gets the undefined value. + * + * @return the undefined value + */ @Override public Object getUndefined() { return UndefinedValue.getUndefined(); } + /** + * Gets the values of the specified object. + * + * @param object the object to retrieve values from + * @return the collection of values + */ @Override public Collection getValues(Object object) { - // TODO: Implement throw new NotImplementedException(this); } + /** + * Checks if the specified object is an array. + * + * @param object the object to check + * @return true if the object is an array, false otherwise + */ @Override public boolean isArray(Object object) { throw new NotImplementedException(this); } + /** + * Checks if the specified object is a boolean. + * + * @param object the object to check + * @return true if the object is a boolean, false otherwise + */ @Override public boolean isBoolean(Object object) { throw new NotImplementedException(this); } + /** + * Checks if the specified object is a function. + * + * @param object the object to check + * @return true if the object is a function, false otherwise + */ @Override public boolean isFunction(Object object) { throw new NotImplementedException(this); } + /** + * Checks if the specified object is a number. + * + * @param object the object to check + * @return true if the object is a number, false otherwise + */ @Override public boolean isNumber(Object object) { throw new NotImplementedException(this); } + /** + * Checks if the specified object is an object. + * + * @param object the object to check + * @return true if the object is an object, false otherwise + */ @Override public boolean isObject(Object object) { throw new NotImplementedException(this); } + /** + * Checks if the specified object is a string. + * + * @param object the object to check + * @return true if the object is a string, false otherwise + */ @Override public boolean isString(Object object) { throw new NotImplementedException(this); } + /** + * Checks if the specified object is undefined. + * + * @param object the object to check + * @return true if the object is undefined, false otherwise + */ @Override public boolean isUndefined(Object object) { throw new NotImplementedException(this); } + /** + * Gets the set of keys from the specified bindings. + * + * @param bindings the bindings to retrieve keys from + * @return the set of keys + */ @Override public Set keySet(Object bindings) { throw new NotImplementedException(this); } + /** + * Creates a new native array. + * + * @return the new native array + */ @Override public Object newNativeArray() { throw new NotImplementedException(this); } + /** + * Creates a new native map. + * + * @return the new native map + */ @Override public Object newNativeMap() { throw new NotImplementedException(this); } + /** + * Puts a value into the specified bindings using the given key. + * + * @param bindings the bindings to put the value into + * @param key the key to use for the value + * @param value the value to put + * @return the previous value associated with the key, or null if there was no mapping + */ @Override public Object put(Object bindings, String key, Object value) { throw new NotImplementedException(this); } + /** + * Puts a value using the given key. + * + * @param key the key to use for the value + * @param value the value to put + */ @Override public void put(String key, Object value) { throw new NotImplementedException(this); } + /** + * Removes a value from the specified bindings using the given key. + * + * @param bindings the bindings to remove the value from + * @param key the key to use for removal + * @return the removed value + */ @Override public Object remove(Object bindings, String key) { throw new NotImplementedException(this); } + /** + * Converts an object to its string representation. + * + * @param object the object to stringify + * @return the string representation of the object + */ @Override public String stringify(Object object) { throw new NotImplementedException(this); } + /** + * Checks if the engine supports properties. + * + * @return true if the engine supports properties, false otherwise + */ @Override public boolean supportsProperties() { throw new NotImplementedException(this); } + /** + * Converts a JavaScript value to a Java object. + * + * @param value the JavaScript value to convert + * @return the converted Java object + */ @Override public Object toJava(Object value) { throw new NotImplementedException(this); } + /** + * Converts a Java object to a JavaScript value. + * + * @param javaObject the Java object to convert + * @return the converted JavaScript value + */ @Override public Object toJs(Object javaObject) { return javaObject; } + /** + * Converts a boolean array to a native array. + * + * @param values the boolean array to convert + * @return the native array + */ @Override public Object toNativeArray(boolean[] values) { return values; } + /** + * Converts a byte array to a native array. + * + * @param values the byte array to convert + * @return the native array + */ @Override public Object toNativeArray(byte[] values) { return values; } + /** + * Converts a char array to a native array. + * + * @param values the char array to convert + * @return the native array + */ @Override public Object toNativeArray(char[] values) { return values; } + /** + * Converts a collection to a native array. + * + * @param values the collection to convert + * @return the native array + */ @Override public Object toNativeArray(Collection values) { return values; } + /** + * Converts a double array to a native array. + * + * @param values the double array to convert + * @return the native array + */ @Override public Object toNativeArray(double[] values) { return values; } + /** + * Converts a float array to a native array. + * + * @param values the float array to convert + * @return the native array + */ @Override public Object toNativeArray(float[] values) { return values; } + /** + * Converts an int array to a native array. + * + * @param values the int array to convert + * @return the native array + */ @Override public Object toNativeArray(int[] values) { return values; } + /** + * Converts a long array to a native array. + * + * @param values the long array to convert + * @return the native array + */ @Override public Object toNativeArray(long[] values) { return values; } + /** + * Converts an object array to a native array. + * + * @param values the object array to convert + * @return the native array + */ @Override public Object toNativeArray(Object[] values) { return values; } + /** + * Converts a short array to a native array. + * + * @param values the short array to convert + * @return the native array + */ @Override public Object toNativeArray(short[] values) { return values; diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/graal/GraalvmBindings.java b/JsEngine/src/pt/up/fe/specs/jsengine/graal/GraalvmBindings.java index d39c6c13..49f7ee79 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/graal/GraalvmBindings.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/graal/GraalvmBindings.java @@ -27,33 +27,38 @@ import org.apache.commons.lang3.tuple.Pair; import org.graalvm.polyglot.Value; +/** + * Utility class for managing bindings in GraalVM JavaScript engine contexts. + */ public class GraalvmBindings implements Bindings { - // private final Bindings bindings; - // private final GraalvmJsEngine engine; private final Value bindings; - // public GraalvmBindings(GraalvmJsEngine engine, Object bindings) { - // this(engine, engine.asValue(bindings)); - // // this.engine = engine; - // // this.bindings = engine.asValue(bindings); - // } - - // public GraalvmBindings(GraalvmJsEngine engine, Value bindings) { + /** + * Constructs a GraalvmBindings instance with the given bindings. + * + * @param bindings the GraalVM Value representing the bindings + */ public GraalvmBindings(Value bindings) { - // this.engine = engine; this.bindings = bindings; - // System.out.println("CONSTRUCTOR VALUE: " + bindings); - // System.out.println("HAS ARRAY ELEM: " + bindings.hasArrayElements()); } + /** + * Retrieves the underlying GraalVM Value object. + * + * @return the GraalVM Value object + */ public Value getValue() { return bindings; } + /** + * Returns the number of elements in the bindings. + * + * @return the size of the bindings + */ @Override public int size() { - if (bindings.hasArrayElements()) { return (int) bindings.getArraySize(); } @@ -63,20 +68,32 @@ public int size() { } return 0; - } + /** + * Checks if the bindings are empty. + * + * @return true if the bindings are empty, false otherwise + */ @Override public boolean isEmpty() { return size() == 0; - // return bindings.getMemberKeys().isEmpty(); } + /** + * Checks if the bindings contain the specified value. + * + * @param value the value to check + * @return true if the value is present, false otherwise + */ @Override public boolean containsValue(Object value) { return values().contains(value); } + /** + * Clears all elements in the bindings. + */ @Override public void clear() { int arraySize = (int) bindings.getArraySize(); @@ -86,9 +103,13 @@ public void clear() { bindings.getMemberKeys().stream() .forEach(bindings::removeMember); - } + /** + * Retrieves the set of keys in the bindings. + * + * @return a set of keys + */ @Override public Set keySet() { if (bindings.hasArrayElements()) { @@ -108,6 +129,11 @@ public Set keySet() { return Collections.emptySet(); } + /** + * Retrieves the collection of values in the bindings. + * + * @return a collection of values + */ @Override public Collection values() { if (bindings.hasArrayElements()) { @@ -129,9 +155,13 @@ public Collection values() { return Collections.emptyList(); } + /** + * Retrieves the set of entries in the bindings. + * + * @return a set of entries + */ @Override public Set> entrySet() { - if (bindings.hasArrayElements()) { Set> set = new HashSet<>(); for (int i = 0; i < bindings.getArraySize(); i++) { @@ -153,6 +183,13 @@ public Set> entrySet() { return Collections.emptySet(); } + /** + * Adds a new key-value pair to the bindings. + * + * @param name the key + * @param value the value + * @return the previous value associated with the key, or null if none + */ @Override public Object put(String name, Object value) { if (bindings.hasArrayElements()) { @@ -164,16 +201,16 @@ public Object put(String name, Object value) { return previousValue; } - // Value valueObject = engine.asValue(bindings); - // Value previousValue = valueObject.getMember(name); - // valueObject.putMember(name, value); - - // Assume object map Value previousValue = bindings.getMember(name); bindings.putMember(name, value); return previousValue; } + /** + * Merges the given map into the bindings. + * + * @param toMerge the map to merge + */ @Override public void putAll(Map toMerge) { if (bindings.hasArrayElements()) { @@ -186,6 +223,12 @@ public void putAll(Map toMerge) { .forEach(entry -> bindings.putMember(entry.getKey(), entry.getValue())); } + /** + * Checks if the bindings contain the specified key. + * + * @param key the key to check + * @return true if the key is present, false otherwise + */ @Override public boolean containsKey(Object key) { if (bindings.hasArrayElements()) { @@ -195,16 +238,27 @@ public boolean containsKey(Object key) { return bindings.hasMember(key.toString()); } + /** + * Retrieves the value associated with the specified key. + * + * @param key the key + * @return the value associated with the key, or null if none + */ @Override public Object get(Object key) { if (bindings.hasArrayElements()) { - // System.out.println("ARRAY GET: " + bindings.getArrayElement(Long.valueOf(key.toString()))); return bindings.getArrayElement(Long.valueOf(key.toString())); } return bindings.getMember(key.toString()); } + /** + * Removes the value associated with the specified key. + * + * @param key the key + * @return the previous value associated with the key, or null if none + */ @Override public Object remove(Object key) { if (bindings.hasArrayElements()) { @@ -219,31 +273,13 @@ public Object remove(Object key) { return previousValue; } + /** + * Returns a string representation of the bindings. + * + * @return a string representation of the bindings + */ @Override public String toString() { - // System.out.println("TOSTRING"); - // Special case for array - /* - if (bindings.hasArrayElements()) { - StringBuilder arrayString = new StringBuilder(); - for (int i = 0; i < bindings.getArraySize(); i++) { - if (i != 0) { - arrayString.append(","); - } - arrayString.append(bindings.getArrayElement(i)); - } - - return arrayString.toString(); - // Object[] list = bindings.as(Object[].class); - // return Arrays.toString(list); - } - */ - - // Undefined - // if (bindings.isNull()) { - // return ""; - // } - return bindings.toString(); } } diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/graal/GraalvmJsEngine.java b/JsEngine/src/pt/up/fe/specs/jsengine/graal/GraalvmJsEngine.java index 24c603ec..912437ac 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/graal/GraalvmJsEngine.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/graal/GraalvmJsEngine.java @@ -51,6 +51,10 @@ import pt.up.fe.specs.util.SpecsLogs; import pt.up.fe.specs.util.exceptions.NotImplementedException; +/** + * JavaScript engine implementation using GraalVM. + * Provides methods for executing JavaScript code in a GraalVM context. + */ public class GraalvmJsEngine extends AJsEngine { private static final String NEW_ARRAY = "[]"; // Faster @@ -59,56 +63,79 @@ public class GraalvmJsEngine extends AJsEngine { private final GraalJSScriptEngine engine; private final Set forbiddenClasses; private final boolean nashornCompatibility; - + + /** + * Constructs a GraalvmJsEngine with the given blacklisted classes. + * + * @param blacklistedClasses a collection of classes to blacklist + */ public GraalvmJsEngine(Collection> blacklistedClasses) { this(blacklistedClasses, false); } + /** + * Constructs a GraalvmJsEngine with the given blacklisted classes and Nashorn compatibility. + * + * @param blacklistedClasses a collection of classes to blacklist + * @param nashornCompatibility whether Nashorn compatibility is enabled + */ public GraalvmJsEngine(Collection> blacklistedClasses, boolean nashornCompatibility) { this(blacklistedClasses, nashornCompatibility, null); } + /** + * Constructs a GraalvmJsEngine with the given blacklisted classes, Nashorn compatibility, and working directory. + * + * @param blacklistedClasses a collection of classes to blacklist + * @param nashornCompatibility whether Nashorn compatibility is enabled + * @param engineWorkingDirectory the working directory for the engine + */ public GraalvmJsEngine(Collection> blacklistedClasses, boolean nashornCompatibility, Path engineWorkingDirectory) { this(blacklistedClasses, nashornCompatibility, engineWorkingDirectory, null, System.out); } + /** + * Constructs a GraalvmJsEngine with the given parameters. + * + * @param blacklistedClasses a collection of classes to blacklist + * @param nashornCompatibility whether Nashorn compatibility is enabled + * @param engineWorkingDirectory the working directory for the engine + * @param nodeModulesFolder the folder containing node modules + * @param laraiOutputStream the output stream for the engine + */ public GraalvmJsEngine(Collection> blacklistedClasses, boolean nashornCompatibility, Path engineWorkingDirectory, File nodeModulesFolder, OutputStream laraiOutputStream) { this.forbiddenClasses = blacklistedClasses.stream().map(Class::getName).collect(Collectors.toSet()); this.nashornCompatibility = nashornCompatibility; - + Context.Builder contextBuilder = createBuilder(engineWorkingDirectory, nodeModulesFolder); - + var baseEngine = Engine.newBuilder() .option("engine.WarnInterpreterOnly", "false") .build(); this.engine = GraalJSScriptEngine.create(baseEngine, contextBuilder); - - this.engine.getContext().setWriter(new PrintWriter(laraiOutputStream, true)); + + this.engine.getContext().setWriter(new PrintWriter(laraiOutputStream, true)); this.engine.getContext().setErrorWriter(new PrintWriter(laraiOutputStream, true)); - - /** - * DO NOT REMOVE - * Code that executes a nothing burger just to force the GraalJSScriptEngine to set the output streams. - * This is needed because we are not using the Script Engine as recommended because we had to do some - * custom stuff. - * - * @see https://github.com/oracle/graaljs/issues/720 - */ + try { engine.eval("42"); } catch (ScriptException e) { throw new RuntimeException(e); } - - // Add rule to ignore polyglot values + addToJsRule(Value.class, this::valueToJs); } + /** + * Converts a GraalVM Value to a JavaScript object. + * + * @param value the GraalVM Value + * @return the JavaScript object + */ private Object valueToJs(Value value) { - // If a host object, convert (is this necessary?) if (value.isHostObject()) { SpecsLogs.debug( () -> "GraalvmJsEngie.valueToJs(): Case where we have a Value that is a host object, check if ok. " @@ -116,42 +143,31 @@ private Object valueToJs(Value value) { return toJs(value.asHostObject()); } - // Otherwise, already converted return value; - } + /** + * Creates a Context.Builder for the GraalVM engine. + * + * @param engineWorkingDirectory the working directory for the engine + * @param nodeModulesFolder the folder containing node modules + * @return the Context.Builder + */ private Context.Builder createBuilder(Path engineWorkingDirectory, File nodeModulesFolder) { - - // var hostAccess = HostAccess.newBuilder() - // .targetTypeMapping(Object.class, Object.class, v -> true, GraalvmJsEngine::test) - // .build(); - Context.Builder contextBuilder = Context.newBuilder("js") - // .allowHostAccess(hostAccess) .allowAllAccess(true) - .allowHostAccess(HostAccess.ALL) - // .option("js.load-from-url", "true") - // .allowIO(IOAccess.ALL) - // .allowCreateThread(true) - // .allowNativeAccess(true) - // .allowPolyglotAccess(PolyglotAccess.ALL) + .allowHostAccess(HostAccess.ALL) .allowHostClassLookup(name -> !forbiddenClasses.contains(name)); if (nodeModulesFolder != null) { FileSystem fs = FileSystem.newDefaultFileSystem(); fs.setCurrentWorkingDirectory(nodeModulesFolder.toPath()); contextBuilder.allowIO(IOAccess.newBuilder().fileSystem(fs).build()); - - // Path path = Paths.get(engineWorkingDirectory + "/node_modules"); - } if (nodeModulesFolder != null) { - // Check folder is called 'node_modules' or if contains a folder 'node_modules' if (!nodeModulesFolder.getName().equals("node_modules") && !(new File(nodeModulesFolder, "node_modules").isDirectory())) { - throw new RuntimeException( "Given node modules folder is not called node_modules, nor contains a node_modules folder: " + nodeModulesFolder.getAbsolutePath()); @@ -161,7 +177,6 @@ private Context.Builder createBuilder(Path engineWorkingDirectory, File nodeModu contextBuilder.option("js.commonjs-require-cwd", nodeModulesFolder.getAbsolutePath()); } - // Set JS version contextBuilder.option("js.ecmascript-version", "2022"); if (this.nashornCompatibility) { @@ -170,6 +185,9 @@ private Context.Builder createBuilder(Path engineWorkingDirectory, File nodeModu return contextBuilder; } + /** + * Constructs a GraalvmJsEngine with no blacklisted classes. + */ public GraalvmJsEngine() { this(Collections.emptyList()); } @@ -185,10 +203,14 @@ public Object eval(String code, JsFileType type, String source) { return eval(graalSource, type); } + /** + * Evaluates JavaScript code with the specified type. + * + * @param graalSource the source builder for the code + * @param type the type of the JavaScript code + * @return the result of the evaluation + */ private Object eval(Builder graalSource, JsFileType type) { - // GraalVM documentation indicates that only .mjs files should be loaded as modules - // https://www.graalvm.org/reference-manual/js/Modules/ - switch (type) { case NORMAL: try { @@ -207,7 +229,6 @@ private Object eval(Builder graalSource, JsFileType type) { default: throw new NotImplementedException(type); } - } @Override @@ -217,79 +238,36 @@ public Value eval(String code) { @Override public Value eval(String code, String source) { - try { - return eval(Source.newBuilder("js", new StringBuilder(code), source) - // .mimeType("application/javascript+module") - .build()); + return eval(Source.newBuilder("js", new StringBuilder(code), source).build()); } catch (IOException e) { throw new RuntimeException("Could not load JS code", e); } } + /** + * Evaluates JavaScript code from a Source object. + * + * @param code the Source object containing the code + * @return the result of the evaluation + */ private Value eval(Source code) { try { - // var writer = new StringWriter(); - // code.getReader().transferTo(writer); - // writer.close(); - // System.out.println("EVAL CODE:\n" + writer.toString()); - - // Value value = asValue(engine.eval(code)); - // Value value = engine.getPolyglotContext().eval("js", code); - Value value = engine.getPolyglotContext().eval(code); - - // if (value.hasMembers() || value.hasArrayElements()) { - // return asBindings(value); - // } - return value; - } catch (PolyglotException e) { - - // System.out.println("CUASE: " + e.getCause()); - // System.out.println("Is host ex? " + ((PolyglotException) e).isHostException()); - // System.out.println("Is guest ex? " + ((PolyglotException) e).isGuestException()); if (e.isHostException()) { - Throwable hostException = null; - try { - hostException = e.asHostException(); - } catch (Exception unreachableException) { - // Will not take this branch since it is a host exception - throw new RuntimeException("Should not launch this exception", unreachableException); - } - - // System.out.println("PE:" + e.getClass()); - // System.out.println("PE CAUSE: " + e.getCause()); - // System.out.println("PE STACK:"); - // e.getPolyglotStackTrace(); - // System.out.println("NORMAL STACK:"); - // e.printStackTrace(); - // System.out.println("HOST:"); - hostException.printStackTrace(); + Throwable hostException = e.asHostException(); throw new RuntimeException(e.getMessage(), hostException); } - - // Throwable currentEx = e; - // // System.out.println("CAUSE: " + e.getCause()); - // while (currentEx != null) { - // currentEx.printStackTrace(); - // currentEx = currentEx.getCause(); - // } - throw new RuntimeException("Polyglot exception while evaluating JavaScript code", e); - } - - catch (Exception e) { - // System.out.println("class: " + e.getClass()); - // e.printStackTrace(); + } catch (Exception e) { throw new RuntimeException("Could not evaluate JavaScript code", e); } } @Override public Object evalFile(File jsFile, JsFileType type, String content) { - var builder = Source.newBuilder("js", jsFile); if (content != null) { builder.content(content); @@ -313,66 +291,32 @@ public Value toNativeArray(Object[] values) { for (int i = 0; i < values.length; i++) { array.setArrayElement(i, toJs(values[i])); } - return array; } - /** - * Based on this site: http://programmaticallyspeaking.com/nashorns-jsobject-in-context.html - * - * @return - */ @Override public Object getUndefined() { var array = engine.getPolyglotContext().eval("js", "[undefined]"); - return array.getArrayElement(0); } @Override public String stringify(Object object) { Value json = eval("JSON"); - return json.invokeMember("stringify", object).toString(); - // return json.invokeMember("stringify", asValue(object).asProxyObject()).toString(); } @Override public Value getBindings() { - // return asValue(engine.getBindings(ScriptContext.ENGINE_SCOPE)); return engine.getPolyglotContext().getBindings("js"); - // return engine.getPolyglotContext().getPolyglotBindings(); - } - - // @Override - // public Object put(Bindings object, String member, Object value) { - // Value valueObject = asValue(object); - // Value previousValue = valueObject.getMember(member); - // valueObject.putMember(member, value); - // return previousValue; - // } - // - // @Override - // public Object remove(Bindings object, Object key) { - // Value valueObject = asValue(object); - // Value previousValue = valueObject.getMember(key.toString()); - // valueObject.removeMember(key.toString()); - // return previousValue; - // } + } - /** - * Adds the members in the given scope before evaluating the code. - */ @Override public Object eval(String code, Object scope, JsFileType type, String source) { - Value scopeValue = asValue(scope); - Map previousValues = new HashMap<>(); - // Add scope code, save previous values for (String key : scopeValue.getMemberKeys()) { - if (asBoolean(eval("typeof variable === 'undefined'"))) { previousValues.put(key, null); } else { @@ -380,25 +324,18 @@ public Object eval(String code, Object scope, JsFileType type, String source) { } Value value = scopeValue.getMember(key); - - // If value is undefined, set the key as undefined if (value.isNull()) { eval(key + " = undefined"); continue; } - - // Otherwise, add the value put(key, value); } - // Execute new code var result = eval(code, type, source); - // Restore previous values for (var entry : previousValues.entrySet()) { var value = entry.getValue(); if (value == null) { - // Should remove entry or is undefined enough? eval(entry.getKey() + " = undefined"); } else { put(entry.getKey(), value); @@ -408,25 +345,29 @@ public Object eval(String code, Object scope, JsFileType type, String source) { return result; } + /** + * Converts an object to a GraalVM Value. + * + * @param object the object to convert + * @return the GraalVM Value + */ public Value asValue(Object object) { if (object instanceof GraalvmBindings) { return ((GraalvmBindings) object).getValue(); } - return engine.getPolyglotContext().asValue(object); } /** - * Convenience method to handle JS maps. - * - * @param value - * @return + * Converts an object to Bindings. + * + * @param value the object to convert + * @return the Bindings */ private Bindings asBindings(Object value) { if (value instanceof GraalvmBindings) { return (Bindings) value; } - return new GraalvmBindings(this.asValue(value)); } @@ -453,7 +394,6 @@ public boolean isNumber(Object object) { @Override public boolean isObject(Object object) { return asValue(object).hasMembers(); - } @Override @@ -464,7 +404,6 @@ public boolean isString(Object object) { @Override public boolean isBoolean(Object object) { return asValue(object).isBoolean(); - } @Override @@ -480,12 +419,6 @@ public Collection getValues(Object object) { return Arrays.asList(value.asHostObject()); } - // // Collection - // if (object instanceof Collection) { - // return (Collection) object; - // } - - // Array if (value.hasArrayElements()) { return LongStream.range(0, value.getArraySize()) .mapToObj(index -> asValue(value.getArrayElement(index))) @@ -493,7 +426,6 @@ public Collection getValues(Object object) { .collect(Collectors.toList()); } - // Map if (value.hasMembers()) { return value.getMemberKeys().stream() .map(key -> asValue(value.getMember(key))) @@ -506,54 +438,42 @@ public Collection getValues(Object object) { @Override public Object toJava(Object jsValue) { - var value = asValue(jsValue); - // Java object if (value.isHostObject()) { return value.asHostObject(); } - // String if (value.isString()) { return value.asString(); } - // Number if (value.isNumber()) { return value.asDouble(); } - // Boolean if (value.isBoolean()) { return value.asBoolean(); } - // Array if (value.hasArrayElements()) { return LongStream.range(0, value.getArraySize()) .mapToObj(index -> toJava(value.getArrayElement(index))) .collect(Collectors.toList()); } - // Map if (value.hasMembers()) { Map map = new LinkedHashMap<>(); - for (var key : value.getMemberKeys()) { map.put(key, toJava(value.getMember(key))); } - return map; } - // null if (value.isNull()) { return null; } - // Jenkins could not find the symbol getMetaQualifiedName() for some reason - // throw new NotImplementedException(value.getMetaQualifiedName()); throw new NotImplementedException("Not implemented for value " + value); } @@ -562,15 +482,8 @@ public T convert(Object object, Class targetClass) { return asValue(object).as(targetClass); } - /// Bindings-like operations - @Override public Object put(Object bindings, String key, Object value) { - // Value bindingsValue = asValue(bindings); - // - // Object previousValue = bindingsValue.getMember(memberName); - // bindingsValue.putMember(memberName, value); - // return previousValue; return asBindings(bindings).put(key, value); } @@ -582,7 +495,6 @@ public Object remove(Object bindings, String key) { @Override public Set keySet(Object bindings) { return asBindings(bindings).keySet(); - } @Override @@ -590,8 +502,6 @@ public Object get(Object bindings, String key) { return asBindings(bindings).get(key); } - /// Engine related engine-scope operations - @Override public void put(String key, Object value) { engine.put(key, value); @@ -599,18 +509,12 @@ public void put(String key, Object value) { @Override public boolean supportsProperties() { - // TODO: use nashorn compatibility return false; } @Override public Optional getException(Object possibleError) { var exception = SpecsPolyglot.getException(possibleError); - /* - if(exception != null) { - exception.printJSStackTrace(); - } - */ return Optional.ofNullable(exception); } @@ -628,7 +532,6 @@ public Object call(Object function, Object... args) { @Override public boolean isFunction(Object object) { var functionValue = asValue(object); - return functionValue.canExecute(); } } diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/libs/EsprimaComment.java b/JsEngine/src/pt/up/fe/specs/jsengine/libs/EsprimaComment.java index 8bf8576c..84967b8e 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/libs/EsprimaComment.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/libs/EsprimaComment.java @@ -20,6 +20,9 @@ import pt.up.fe.specs.util.SpecsCheck; +/** + * Represents a comment node in an Esprima AST. + */ public class EsprimaComment { private static final EsprimaComment EMPTY = new EsprimaComment(new HashMap<>()); @@ -29,19 +32,39 @@ public class EsprimaComment { private final Map comment; + /** + * Constructs an EsprimaComment instance with the given comment data. + * + * @param comments a map containing the comment data + */ public EsprimaComment(Map comments) { this.comment = comments; } + /** + * Returns an empty EsprimaComment instance. + * + * @return an empty EsprimaComment + */ public static EsprimaComment empty() { return EMPTY; } + /** + * Returns a string representation of the comment. + * + * @return the string representation of the comment + */ @Override public String toString() { return comment.toString(); } + /** + * Retrieves the location information of the comment. + * + * @return an EsprimaLoc instance representing the location of the comment + */ public EsprimaLoc getLoc() { @SuppressWarnings("unchecked") var loc = (Map) comment.get("loc"); @@ -53,18 +76,33 @@ public EsprimaLoc getLoc() { return EsprimaLoc.newInstance(loc); } + /** + * Retrieves the contents of the comment. + * + * @return the contents of the comment, or an empty string if not available + */ public String getContents() { var content = (String) comment.get("value"); return content != null ? content : ""; } + /** + * Retrieves the type of the comment. + * + * @return the type of the comment + */ public String getType() { var type = (String) comment.get("type"); SpecsCheck.checkNotNull(type, () -> "Comment should have type"); return type; } + /** + * Generates the code representation of the comment based on its type and contents. + * + * @return the code representation of the comment + */ public String getCode() { if (this == EMPTY) { return ""; diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/libs/EsprimaLoc.java b/JsEngine/src/pt/up/fe/specs/jsengine/libs/EsprimaLoc.java index feeba1b5..b7accdad 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/libs/EsprimaLoc.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/libs/EsprimaLoc.java @@ -15,6 +15,9 @@ import java.util.Map; +/** + * Represents the location information for an Esprima AST node. + */ public class EsprimaLoc { private static final EsprimaLoc UNDEFINED = new EsprimaLoc(-1, -1, -1, -1); @@ -24,6 +27,14 @@ public class EsprimaLoc { private final int endLine; private final int endCol; + /** + * Constructs an EsprimaLoc object with the given start and end line/column information. + * + * @param startLine the starting line number + * @param startCol the starting column number + * @param endLine the ending line number + * @param endCol the ending column number + */ public EsprimaLoc(int startLine, int startCol, int endLine, int endCol) { this.startLine = startLine; this.startCol = startCol; @@ -31,6 +42,12 @@ public EsprimaLoc(int startLine, int startCol, int endLine, int endCol) { this.endCol = endCol; } + /** + * Creates a new instance of EsprimaLoc from a map containing location information. + * + * @param loc a map with "start" and "end" keys containing line and column information + * @return a new EsprimaLoc object + */ public static EsprimaLoc newInstance(Map loc) { @SuppressWarnings("unchecked") var start = (Map) loc.get("start"); @@ -42,35 +59,37 @@ public static EsprimaLoc newInstance(Map loc) { } /** - * @return the startLine + * @return the starting line number */ public int getStartLine() { return startLine; } /** - * @return the startCol + * @return the starting column number */ public int getStartCol() { return startCol; } /** - * @return the endLine + * @return the ending line number */ public int getEndLine() { return endLine; } /** - * @return the endCol + * @return the ending column number */ public int getEndCol() { return endCol; } - /* (non-Javadoc) - * @see java.lang.Object#toString() + /** + * Returns a string representation of the EsprimaLoc object. + * + * @return a string describing the location information */ @Override public String toString() { @@ -78,6 +97,11 @@ public String toString() { + endCol + "]"; } + /** + * Returns an undefined EsprimaLoc object. + * + * @return an EsprimaLoc object representing undefined location + */ public static EsprimaLoc undefined() { return UNDEFINED; } diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/libs/EsprimaNode.java b/JsEngine/src/pt/up/fe/specs/jsengine/libs/EsprimaNode.java index 5dc66f5a..5ddf0b78 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/libs/EsprimaNode.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/libs/EsprimaNode.java @@ -25,16 +25,29 @@ import pt.up.fe.specs.util.SpecsCheck; +/** + * Represents a node in an Esprima AST. + */ public class EsprimaNode { private static final Set IGNORE_KEYS = new HashSet<>(Arrays.asList("type", "loc", "comments")); private final Map node; + /** + * Constructs an EsprimaNode with the given node map. + * + * @param node the map representing the node + */ public EsprimaNode(Map node) { this.node = node; } + /** + * Retrieves the immediate children of this node. + * + * @return a list of child nodes + */ public List getChildren() { var children = new ArrayList(); @@ -82,27 +95,59 @@ public List getChildren() { return children; } + /** + * Retrieves all descendants of this node. + * + * @return a list of descendant nodes + */ public List getDescendants() { return getDescendantsStream().collect(Collectors.toList()); } + /** + * Retrieves a stream of immediate children of this node. + * + * @return a stream of child nodes + */ public Stream getChildrenStream() { return getChildren().stream(); } + /** + * Retrieves a stream of all descendants of this node. + * + * @return a stream of descendant nodes + */ public Stream getDescendantsStream() { return getChildrenStream() .flatMap(c -> c.getDescendantsAndSelfStream()); } + /** + * Retrieves a stream of this node and all its descendants. + * + * @return a stream of this node and its descendants + */ public Stream getDescendantsAndSelfStream() { return Stream.concat(Stream.of(this), getDescendantsStream()); } + /** + * Retrieves the value associated with the given key. + * + * @param key the key to look up + * @return the value associated with the key + */ public Object get(String key) { return node.get(key); } + /** + * Retrieves the type of this node. + * + * @return the type of the node + * @throws RuntimeException if the node does not have a type + */ public String getType() { if (!node.containsKey("type")) { throw new RuntimeException("Node does not have type: " + node); @@ -111,10 +156,20 @@ public String getType() { return (String) node.get("type"); } + /** + * Checks if this node has comments. + * + * @return true if the node has comments, false otherwise + */ public boolean hasComment() { return node.containsKey("comments"); } + /** + * Retrieves the comments associated with this node. + * + * @return a list of comments + */ @SuppressWarnings("unchecked") public List getComments() { return getAsList("comments", Map.class).stream() @@ -122,6 +177,15 @@ public List getComments() { .collect(Collectors.toList()); } + /** + * Retrieves the value associated with the given key as a list of the specified type. + * + * @param key the key to look up + * @param elementType the class of the elements in the list + * @param the type of the elements in the list + * @return a list of elements of the specified type + * @throws RuntimeException if the value is not a list + */ public List getAsList(String key, Class elementType) { var value = node.get(key); @@ -139,6 +203,12 @@ public List getAsList(String key, Class elementType) { return list.stream().map(elementType::cast).collect(Collectors.toList()); } + /** + * Retrieves the value associated with the given key as an EsprimaNode. + * + * @param key the key to look up + * @return the EsprimaNode associated with the key + */ public EsprimaNode getAsNode(String key) { var value = getExistingValue(key, Map.class); @SuppressWarnings("unchecked") @@ -146,6 +216,12 @@ public EsprimaNode getAsNode(String key) { return node; } + /** + * Retrieves the value associated with the given key as a list of EsprimaNodes. + * + * @param key the key to look up + * @return a list of EsprimaNodes associated with the key + */ @SuppressWarnings("unchecked") public List getAsNodes(String key) { var values = getExistingValue(key, List.class); @@ -155,32 +231,40 @@ public List getAsNodes(String key) { values.stream() .forEach(value -> nodes.add(new EsprimaNode((Map) value))); - // var a = values.stream() - // .map(value -> new EsprimaNode((Map) value)) - // .collect(Collectors.toList()); - return nodes; - - // return values.stream() - // .map(value -> (Map) value) - // .map(value -> new EsprimaNode(value)) - // .collect(Collectors.toList()); - // @SuppressWarnings("unchecked") - // var node = new EsprimaNode(value); - // return node; } + /** + * Retrieves the value associated with the given key, ensuring it exists and is of the specified type. + * + * @param key the key to look up + * @param valueClass the class of the value + * @param the type of the value + * @return the value associated with the key + * @throws RuntimeException if the value does not exist or is not of the specified type + */ private T getExistingValue(String key, Class valueClass) { var value = node.get(key); SpecsCheck.checkNotNull(value, () -> "Expected value with key '" + key + "' to exist"); return valueClass.cast(value); } + /** + * Returns a string representation of this node. + * + * @return a string representation of the node + */ @Override public String toString() { return node.toString(); } + /** + * Retrieves the location information of this node. + * + * @return the location information + * @throws RuntimeException if the location information is null + */ public EsprimaLoc getLoc() { @SuppressWarnings("unchecked") var loc = (Map) node.get("loc"); @@ -188,10 +272,20 @@ public EsprimaLoc getLoc() { return EsprimaLoc.newInstance(loc); } + /** + * Sets the comment for this node. + * + * @param comment the comment to set + */ public void setComment(EsprimaComment comment) { node.put("comments", comment); } + /** + * Retrieves the comment associated with this node. + * + * @return the comment associated with the node, or an empty comment if none exists + */ public EsprimaComment getComment() { if (!node.containsKey("comments")) { return EsprimaComment.empty(); @@ -200,22 +294,50 @@ public EsprimaComment getComment() { return (EsprimaComment) node.get("comments"); } + /** + * Retrieves the keys of this node. + * + * @return a set of keys + */ public Set getKeys() { return node.keySet(); } + /** + * Retrieves the value associated with the given key as a string. + * + * @param key the key to look up + * @return the value associated with the key as a string + */ public String getAsString(String key) { return getExistingValue(key, String.class); } + /** + * Retrieves the underlying map of this node. + * + * @return the map representing the node + */ public Map getNode() { return node; } + /** + * Retrieves the value associated with the given key as a boolean. + * + * @param key the key to look up + * @return the value associated with the key as a boolean + */ public boolean getAsBool(String key) { return getExistingValue(key, Boolean.class); } + /** + * Checks if this node has a value for the given key. + * + * @param key the key to check + * @return true if the node has a value for the key, false otherwise + */ public boolean hasValueFor(String key) { return node.get(key) != null; } diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/libs/JsBabel.java b/JsEngine/src/pt/up/fe/specs/jsengine/libs/JsBabel.java index 7d50d46d..70fe2181 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/libs/JsBabel.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/libs/JsBabel.java @@ -18,11 +18,19 @@ import pt.up.fe.specs.jsengine.JsEngineWebResources; import pt.up.fe.specs.util.SpecsIo; +/** + * Utility class for working with Babel JavaScript transpiler. + */ public class JsBabel { private static final ThreadLocal BABEL_ENGINE = ThreadLocal .withInitial(JsBabel::newBabelEngine); + /** + * Creates a new instance of the Babel JavaScript engine. + * + * @return a new JsEngine instance configured for Babel + */ private static JsEngine newBabelEngine() { // Create JsEngine var engine = JsEngineType.GRAALVM.newEngine(); @@ -35,18 +43,27 @@ private static JsEngine newBabelEngine() { engine.eval(SpecsIo.read(babelSource.getFile())); // Load toES6 function - // Using Chrome 58 as target due to being the value that appears as example in Babel documentation, and Esprima - // apparently supporting it engine.eval( "function toES6(code) {return Babel.transform(code, { presets: [\"env\"], targets: {\"chrome\": \"58\"} }).code;}"); return engine; } + /** + * Retrieves the current Babel engine instance. + * + * @return the current JsEngine instance + */ private static JsEngine getEngine() { return BABEL_ENGINE.get(); } + /** + * Transforms the given JavaScript code to ES6 using Babel. + * + * @param jsCode the JavaScript code to transform + * @return the transformed ES6 code + */ public static String toES6(String jsCode) { var toEs5Function = getEngine().get("toES6"); var es5Code = getEngine().call(toEs5Function, jsCode); diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/libs/JsEsprima.java b/JsEngine/src/pt/up/fe/specs/jsengine/libs/JsEsprima.java index 9b463e44..8fd5962d 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/libs/JsEsprima.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/libs/JsEsprima.java @@ -23,11 +23,19 @@ import pt.up.fe.specs.util.SpecsIo; import pt.up.fe.specs.util.SpecsLogs; +/** + * Utility class for working with the Esprima JavaScript parser. + */ public class JsEsprima { private static final ThreadLocal ESPRIMA_ENGINE = ThreadLocal .withInitial(JsEsprima::newEsprimaEngine); + /** + * Creates a new instance of the Esprima JavaScript engine. + * + * @return a new JsEngine instance configured with Esprima + */ private static JsEngine newEsprimaEngine() { // Create JsEngine var engine = JsEngineType.GRAALVM.newEngine(); @@ -46,14 +54,32 @@ private static JsEngine newEsprimaEngine() { return engine; } + /** + * Retrieves the current thread-local instance of the Esprima engine. + * + * @return the current JsEngine instance + */ private static JsEngine getEngine() { return ESPRIMA_ENGINE.get(); } + /** + * Parses the given JavaScript code and returns the corresponding AST. + * + * @param jsCode the JavaScript code to parse + * @return the root node of the parsed AST + */ public static EsprimaNode parse(String jsCode) { return parse(jsCode, ""); } + /** + * Parses the given JavaScript code and returns the corresponding AST, associating it with the provided source path. + * + * @param jsCode the JavaScript code to parse + * @param path the source path associated with the code + * @return the root node of the parsed AST + */ @SuppressWarnings("unchecked") public static EsprimaNode parse(String jsCode, String path) { var engine = getEngine(); @@ -80,6 +106,11 @@ public static EsprimaNode parse(String jsCode, String path) { return program; } + /** + * Associates comments with the corresponding nodes in the AST. + * + * @param program the root node of the AST + */ private static void associateComments(EsprimaNode program) { var comments = program.getComments(); @@ -92,15 +123,12 @@ private static void associateComments(EsprimaNode program) { var commentsIterator = comments.iterator(); var currentComment = commentsIterator.next(); - // System.out.println("COMMENT LOC: " + currentComment.getLoc()); NODES: for (var node : nodes) { var nodeLoc = node.getLoc(); - // System.out.println("NODE LOC: " + nodeLoc); // If node start line is the same or greater than the comment, associate node with comment while (nodeLoc.getStartLine() >= currentComment.getLoc().getStartLine()) { - // System.out.println("FOUND ASSOCIATION: " + node.getType() + " @ " + node.getLoc()); node.setComment(currentComment); if (!commentsIterator.hasNext()) { @@ -108,7 +136,6 @@ private static void associateComments(EsprimaNode program) { } currentComment = commentsIterator.next(); - // System.out.println("COMMENT LOC: " + currentComment.getLoc()); } } } diff --git a/JsEngine/src/pt/up/fe/specs/jsengine/node/UndefinedValue.java b/JsEngine/src/pt/up/fe/specs/jsengine/node/UndefinedValue.java index d04f529d..3f1b1066 100644 --- a/JsEngine/src/pt/up/fe/specs/jsengine/node/UndefinedValue.java +++ b/JsEngine/src/pt/up/fe/specs/jsengine/node/UndefinedValue.java @@ -12,18 +12,28 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * under the License. */ package pt.up.fe.specs.jsengine.node; /** - * Dummy class to represent javascript's "undefined" in Java. Used by NodeJsEngine as a return value. + * Singleton class representing an undefined value in JavaScript engine integration. + *

+ * This class is used to represent JavaScript's "undefined" in Java. It is primarily utilized by + * NodeJsEngine as a return value when JavaScript code evaluates to "undefined". */ public class UndefinedValue { + /** + * A single instance of UndefinedValue to ensure singleton behavior. + */ private static final UndefinedValue UNDEFINED = new UndefinedValue(); + /** + * Retrieves the singleton instance of UndefinedValue. + * + * @return the singleton instance of UndefinedValue + */ public static UndefinedValue getUndefined() { return UNDEFINED; } diff --git a/LogbackPlus/src/pt/up/fe/specs/logback/SpecsLogbackResource.java b/LogbackPlus/src/pt/up/fe/specs/logback/SpecsLogbackResource.java index af87d0cb..ab910b49 100644 --- a/LogbackPlus/src/pt/up/fe/specs/logback/SpecsLogbackResource.java +++ b/LogbackPlus/src/pt/up/fe/specs/logback/SpecsLogbackResource.java @@ -1,29 +1,45 @@ /** * Copyright 2022 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.logback; import pt.up.fe.specs.util.providers.ResourceProvider; +/** + * Enum representing Logback resource files for SPeCS projects. + */ public enum SpecsLogbackResource implements ResourceProvider { + /** + * The default logback.xml resource. + */ LOGBACK_XML("logback.xml"); private final String resource; + /** + * Constructs a SpecsLogbackResource with the given resource name. + * + * @param resource the resource file name + */ private SpecsLogbackResource(String resource) { this.resource = resource; } + /** + * Returns the resource file name. + * + * @return the resource file name + */ @Override public String getResource() { return resource; diff --git a/SpecsUtils/README.md b/SpecsUtils/README.md new file mode 100644 index 00000000..e662477e --- /dev/null +++ b/SpecsUtils/README.md @@ -0,0 +1,42 @@ +# SpecsUtils + +SpecsUtils is a Java utility library developed by the SPeCS Research Group. It provides a comprehensive set of static utility classes and methods to simplify and extend Java development, especially for projects in the SPeCS ecosystem. The library covers a wide range of functionalities, including: + +- **Collections**: Helpers for lists, maps, sets, and other collection types. +- **I/O**: File and resource reading/writing utilities. +- **Strings and Numbers**: Parsing, formatting, and manipulation. +- **Logging**: Unified logging API with support for custom handlers and output redirection. +- **XML**: Parsing, validation, and DOM manipulation. +- **Reflection**: Utilities for inspecting and manipulating classes, methods, and fields at runtime. +- **Threading**: Thread management and concurrency helpers. +- **Providers**: Interfaces and helpers for resource and key providers. +- **Date, Math, Random, Path, and more**: Utilities for common programming tasks. + +## Features +- Consistent API and coding style across all utilities +- Designed for extensibility and integration with SPeCS tools +- Includes deprecated methods for backward compatibility +- Well-documented with Javadoc and file-level comments + +## Usage +Add SpecsUtils as a dependency in your Java project. You can then use the static methods directly, for example: + +```java +import pt.up.fe.specs.util.SpecsCollections; + +List sublist = SpecsCollections.subList(myList, 2); +``` + +## Project Structure +- `src/pt/up/fe/specs/util/` - Main utility classes +- `src/pt/up/fe/specs/util/collections/` - Collection-related helpers +- `src/pt/up/fe/specs/util/providers/` - Provider interfaces and helpers +- `src/pt/up/fe/specs/util/reporting/` - Reporting and logging interfaces +- `src/pt/up/fe/specs/util/xml/` - XML utilities +- ...and more + +## License +SpecsUtils is licensed under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for details. + +## Authors +Developed and maintained by the SPeCS Research Group at FEUP. diff --git a/SpecsUtils/src/pt/up/fe/specs/util/DotRenderFormat.java b/SpecsUtils/src/pt/up/fe/specs/util/DotRenderFormat.java index 00f67f6a..ee3d3ec0 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/DotRenderFormat.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/DotRenderFormat.java @@ -1,11 +1,11 @@ -/** - * Copyright 2021 SPeCS. - * +/* + * Copyright 2021 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -13,15 +13,44 @@ package pt.up.fe.specs.util; +/** + * Enum for specifying DOT rendering formats. + *

+ * Used for selecting output formats in graph rendering utilities. + *

+ */ public enum DotRenderFormat { + /** + * PNG format for rendering DOT files. + */ PNG, + + /** + * SVG format for rendering DOT files. + */ SVG; + /** + * Gets the flag associated with the rendering format. + *

+ * The flag is used in graph rendering utilities to specify the output format. + *

+ * + * @return the flag for the rendering format + */ public String getFlag() { return "-T" + name().toLowerCase(); } + /** + * Gets the file extension associated with the rendering format. + *

+ * The extension is used for naming output files generated by graph rendering utilities. + *

+ * + * @return the file extension for the rendering format + */ public String getExtension() { return name().toLowerCase(); } diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsAsm.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsAsm.java index 63dee0a4..78d0c174 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsAsm.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsAsm.java @@ -1,11 +1,11 @@ /* * Copyright 2011 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -16,35 +16,46 @@ import pt.up.fe.specs.util.asm.ArithmeticResult32; /** - * Utility methods related with solving operations. + * Utility methods for assembly code operations. + *

+ * Provides static helper methods for parsing, formatting, and manipulating assembly code. + *

* * @author Joao Bispo */ public class SpecsAsm { + /** + * Adds two 64-bit integers and a carry value. + * + * @param input1 the first operand + * @param input2 the second operand + * @param carry the carry value (0 or 1) + * @return the result of the addition + */ public static long add64(long input1, long input2, long carry) { return input1 + input2 + carry; } + /** + * Performs reverse subtraction on two 64-bit integers and a carry value. + * + * @param input1 the first operand + * @param input2 the second operand + * @param carry the carry value (0 or 1) + * @return the result of the reverse subtraction + */ public static long rsub64(long input1, long input2, long carry) { return input2 + ~input1 + carry; } - /* - public static ArithmeticResult32 add32(int firstTerm, int secondTerm) { - return add32(firstTerm, secondTerm, CARRY_NEUTRAL_ADD); - } - * - */ - /** * Calculates the carryOut of the sum of rA with rB and carry. Operation is rA + rB + carry. * - * @param input1 - * @param input2 - * @param carry - * the carry from the previous operation. Should be 0 or 1. - * @return 1 if there is carry out, or 0 if not. + * @param input1 the first operand + * @param input2 the second operand + * @param carry the carry from the previous operation. Should be 0 or 1. + * @return an ArithmeticResult32 containing the result and carry out */ public static ArithmeticResult32 add32(int input1, int input2, int carry) { if (carry != 0 && carry != 1) { @@ -70,11 +81,10 @@ public static ArithmeticResult32 add32(int input1, int input2, int carry) { /** * Calculates the carryOut of the reverse subtraction of rA with rB and carry. Operation is rB + ~rA + carry. * - * @param input1 - * @param input2 - * @param carry - * the carry from the previous operation. Should be 0 or 1. - * @return 1 if there is carry out, or 0 if not. + * @param input1 the first operand + * @param input2 the second operand + * @param carry the carry from the previous operation. Should be 0 or 1. + * @return an ArithmeticResult32 containing the result and carry out */ public static ArithmeticResult32 rsub32(int input1, int input2, int carry) { if (carry != 0 && carry != 1) { @@ -89,7 +99,6 @@ public static ArithmeticResult32 rsub32(int input1, int input2, int carry) { long lCarry = carry; // Do the summation - // long result = lRb + ~lRa + lCarry; long result = rsub64(lRa, lRb, lCarry); int maskedResult = (int) result; @@ -98,33 +107,67 @@ public static ArithmeticResult32 rsub32(int input1, int input2, int carry) { return new ArithmeticResult32(maskedResult, carryOut); } - /* - public static ArithmeticResult32 rsub32(int firstTerm, int secondTerm) { - return rsub32(firstTerm, secondTerm, CARRY_NEUTRAL_SUB); - } - * + /** + * Performs a bitwise AND operation on two 32-bit integers. + * + * @param input1 the first operand + * @param input2 the second operand + * @return the result of the AND operation */ - public static int and32(int input1, int input2) { return input1 & input2; } + /** + * Performs a bitwise AND NOT operation on two 32-bit integers. + * + * @param input1 the first operand + * @param input2 the second operand + * @return the result of the AND NOT operation + */ public static int andNot32(int input1, int input2) { return input1 & ~input2; } + /** + * Performs a bitwise NOT operation on a 32-bit integer. + * + * @param input1 the operand + * @return the result of the NOT operation + */ public static int not32(int input1) { return ~input1; } + /** + * Performs a bitwise OR operation on two 32-bit integers. + * + * @param input1 the first operand + * @param input2 the second operand + * @return the result of the OR operation + */ public static int or32(int input1, int input2) { return input1 | input2; } + /** + * Performs a bitwise XOR operation on two 32-bit integers. + * + * @param input1 the first operand + * @param input2 the second operand + * @return the result of the XOR operation + */ public static int xor32(int input1, int input2) { return input1 ^ input2; } + /** + * Compares two signed 32-bit integers and modifies the MSB to reflect the relation. + * + * @param input1 the first operand + * @param input2 the second operand + * @return the result of the comparison + */ public static int mbCompareSigned(int input1, int input2) { int result = input2 + ~input1 + 1; boolean aBiggerThanB = input1 > input2; @@ -136,6 +179,13 @@ public static int mbCompareSigned(int input1, int input2) { return SpecsBits.clearBit(31, result); } + /** + * Compares two unsigned 32-bit integers and modifies the MSB to reflect the relation. + * + * @param input1 the first operand + * @param input2 the second operand + * @return the result of the comparison + */ public static int mbCompareUnsigned(int input1, int input2) { int result = input2 + ~input1 + 1; boolean aBiggerThanB = SpecsBits.unsignedComp(input1, input2); @@ -148,42 +198,86 @@ public static int mbCompareUnsigned(int input1, int input2) { return SpecsBits.clearBit(31, result); } + /** + * Performs a logical left shift on a 32-bit integer. + * + * @param input1 the operand to shift + * @param input2 the number of positions to shift + * @return the result of the shift + */ public static int shiftLeftLogical(int input1, int input2) { return input1 << input2; } + /** + * Performs an arithmetic right shift on a 32-bit integer. + * + * @param input1 the operand to shift + * @param input2 the number of positions to shift + * @return the result of the shift + */ public static int shiftRightArithmetical(int input1, int input2) { return input1 >> input2; } + /** + * Performs a logical right shift on a 32-bit integer. + * + * @param input1 the operand to shift + * @param input2 the number of positions to shift + * @return the result of the shift + */ public static int shiftRightLogical(int input1, int input2) { return input1 >>> input2; } /** + * Performs a logical left shift on a 32-bit integer, taking into account a mask. * - * @param input1 - * @param input2 - * @param input3 - * the number of LSB bits of input2 to take into account - * @return + * @param input1 the operand to shift + * @param input2 the number of positions to shift + * @param input3 the number of LSB bits of input2 to take into account + * @return the result of the shift */ public static int shiftLeftLogical(int input1, int input2, int input3) { input2 = SpecsBits.mask(input2, input3); return input1 << input2; } + /** + * Performs an arithmetic right shift on a 32-bit integer, taking into account a mask. + * + * @param input1 the operand to shift + * @param input2 the number of positions to shift + * @param input3 the number of LSB bits of input2 to take into account + * @return the result of the shift + */ public static int shiftRightArithmetical(int input1, int input2, int input3) { input2 = SpecsBits.mask(input2, input3); return input1 >> input2; } + /** + * Performs a logical right shift on a 32-bit integer, taking into account a mask. + * + * @param input1 the operand to shift + * @param input2 the number of positions to shift + * @param input3 the number of LSB bits of input2 to take into account + * @return the result of the shift + */ public static int shiftRightLogical(int input1, int input2, int input3) { input2 = SpecsBits.mask(input2, input3); return input1 >>> input2; } + /** + * Neutral carry value for addition operations. + */ public static final int CARRY_NEUTRAL_ADD = 0; + + /** + * Neutral carry value for subtraction operations. + */ public static final int CARRY_NEUTRAL_SUB = 1; } diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsBits.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsBits.java index ce2aa38c..0aa5954d 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsBits.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsBits.java @@ -1,11 +1,11 @@ /* * Copyright 2009 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -15,8 +15,11 @@ import java.nio.ByteBuffer; /** - * Methods for bit manipulation. - * + * Utility methods for bitwise operations. + *

+ * Provides static helper methods for manipulating bits and binary representations. + *

+ * * @author Joao Bispo */ public class SpecsBits { @@ -24,33 +27,88 @@ public class SpecsBits { // / // CONSTANTS // / + + /** + * String representation of zero. + */ private static final String ZERO = "0"; + + /** + * Prefix for hexadecimal numbers. + */ private static final String HEX_PREFIX = "0x"; + + /** + * Mask for 16 bits. + */ private static final long MASK_16_BITS = 0xFFFFL; + + /** + * Mask for 32 bits. + */ private static final long MASK_32_BITS = 0xFFFFFFFFL; + + /** + * Mask for the 33rd bit. + */ private static final long MASK_BIT_33 = 0x100000000L; + + /** + * Mask for the least significant bit. + */ private static final int MASK_BIT_1 = 0x1; - // public static final short UNSIGNED_BYTE_MASK = 0x00FF; + + /** + * Mask for unsigned byte. + */ private static final int UNSIGNED_BYTE_MASK = 0x000000FF; // Floating-point related constants - // Bits 30 to 23 set. + /** + * Mask for denormalized floating-point numbers. + */ private static final int DENORMAL_MASK = 0x7F800000; - // Bits 30 to 23 set. + + /** + * Mask for non-sign bits in floating-point numbers. + */ private static final int NOT_SIGN_MASK = 0x7FFFFFFF; - // Bits 30 to 23 set. + + /** + * Mask for zero floating-point numbers. + */ private static final int ZERO_MASK = 0x7FFFFFFF; + /** + * Mask for the sign bit in floating-point numbers. + */ private static final int FLOAT_SIGN_MASK = 0x80000000; + + /** + * Representation of infinity in floating-point numbers. + */ private static final int FLOAT_INFINITY = 0x7F800000; + /** + * Number of bits in a byte. + */ private static final int BITS_IN_A_BYTE = 8; + /** + * Returns the mask for 32 bits. + * + * @return the mask for 32 bits + */ public static long getMask32Bits() { return MASK_32_BITS; } + /** + * Returns the mask for the 33rd bit. + * + * @return the mask for the 33rd bit + */ public static long getMaskBit33() { return MASK_BIT_33; } @@ -342,9 +400,14 @@ public static int fuseImm(int upper16, int lower16) { return result; } + /** + * Converts a signed byte to an unsigned integer representation. + * + * @param aByte + * the byte to convert + * @return the unsigned integer representation of the byte + */ public static int getUnsignedByte(byte aByte) { - // short byteAsShort = aByte; - // return (short) (byteAsShort & UNSIGNED_BYTE_MASK); int byteAsInt = aByte; // When casting a byte to an int, if the byte is signed the additional // bits will be set to 1. @@ -354,8 +417,11 @@ public static int getUnsignedByte(byte aByte) { } /** + * Calculates the base-2 logarithm of the given integer, rounding up. + * * @param i - * @return log2 of the given integer. Rounds up + * the integer to calculate the logarithm for + * @return the base-2 logarithm of the integer, rounded up */ public static int log2(int i) { double log2 = Math.log(i) / Math.log(2); @@ -445,6 +511,13 @@ public static String signExtend(String binaryValue, int signalBit) { + binaryValue.substring(lsbSignalIndex + 1, binaryValue.length()); } + /** + * Parses a signed binary string into an integer. + * + * @param binaryString + * the binary string to parse + * @return the integer representation of the binary string + */ public static int parseSignedBinary(String binaryString) { if (binaryString.length() > 32) { SpecsLogs.warn("Given string has more than 32 bits. Truncating MSB."); @@ -498,8 +571,10 @@ public static int mask(int value, int numBits) { } /** + * Converts a boolean value to an integer representation. * * @param boolResult + * the boolean value to convert * @return 1 if true, or 0 if false */ public static int boolToInt(boolean boolResult) { @@ -606,6 +681,15 @@ public static int getShort(int value, int byteOffset) { } } + /** + * Extracts a specific byte from an integer. + * + * @param value + * the integer to extract the byte from + * @param byteOffset + * the offset of the byte to extract (0-based) + * @return the extracted byte as an integer + */ public static int getByte(int value, int byteOffset) { switch (byteOffset) { case 0: @@ -651,6 +735,18 @@ public static int readUnsignedShort(byte[] byteArray, int offset, */ } + /** + * Reads an unsigned 32-bit number from a byte array. This method reads four bytes from the array, starting at the + * given offset. + * + * @param byteArray + * the byte array to read from + * @param offset + * the starting offset in the array + * @param isLittleEndian + * whether the bytes are in little-endian order + * @return the unsigned 32-bit number as a long + */ public static long readUnsignedInteger(byte[] byteArray, int offset, boolean isLittleEndian) { @@ -664,7 +760,7 @@ public static long readUnsignedInteger(byte[] byteArray, int offset, } /** - * Positions an byte inside a bigger unit according to its endianess and the position of the byte. A long is used to + * Positions a byte inside a bigger unit according to its endianess and the position of the byte. A long is used to * support unsigned integers. * * TODO: Test/check this method so see if it can support longs, not just integers @@ -695,6 +791,13 @@ public static long positionByte(byte aByte, int bytePosition, int totalBytes, bo return shiftedByte; } + /** + * Reverses the half-words in the given integer. + * + * @param data + * the integer to reverse the half-words of + * @return the integer with reversed half-words + */ public static int reverseHalfWords(int data) { int higherHalf = data << 16; @@ -704,10 +807,11 @@ public static int reverseHalfWords(int data) { } /** - * Reverses the bytes on the given int. + * Reverses the bytes in the given integer. * * @param data - * @return + * the integer to reverse the bytes of + * @return the integer with reversed bytes */ public static int reverse(int data) { // Reverse bytes of data @@ -726,6 +830,13 @@ public static int reverse(int data) { return wrapped.getInt(); } + /** + * Reverses the bytes in the given short. + * + * @param data + * the short to reverse the bytes of + * @return the short with reversed bytes + */ public static short reverse(short data) { // Reverse bytes of data byte[] bytes = ByteBuffer.allocate(2).putShort(data).array(); @@ -739,10 +850,11 @@ public static short reverse(short data) { } /** - * Reverses an array of bytes. + * Reverses the order of bytes in the given array. * * @param bytes - * @return + * the array of bytes to reverse + * @return the array with reversed byte order */ public static byte[] reverse(byte[] bytes) { byte[] reversedBytes = new byte[bytes.length]; @@ -753,6 +865,13 @@ public static byte[] reverse(byte[] bytes) { return reversedBytes; } + /** + * Decodes an unsigned byte value from a string representation. + * + * @param unsignedByteValue + * the string representation of the unsigned byte value + * @return the decoded unsigned byte value + */ public static byte decodeUnsignedByte(String unsignedByteValue) { // Bytes in Java are signed, decode as Short return Short.valueOf(unsignedByteValue).byteValue(); diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsCheck.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsCheck.java index 7b6d6d7f..b3c515b9 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsCheck.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsCheck.java @@ -1,11 +1,11 @@ -/** - * Copyright 2018 SPeCS. - * +/* + * Copyright 2018 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -18,22 +18,38 @@ import java.util.function.Supplier; /** - * Utility class with methods for checkers. - * - * @author JoaoBispo + * Utility methods for runtime checks and assertions. + *

+ * Provides static helper methods for validating arguments, states, and error conditions at runtime. + *

* + * @author Joao Bispo */ public class SpecsCheck { private SpecsCheck() { } + /** + * Validates that a given expression is true. Throws an IllegalArgumentException if the expression is false. + * + * @param expression the condition to validate + * @param supplier a supplier providing the error message + */ public static void checkArgument(boolean expression, Supplier supplier) { if (!expression) { throw new IllegalArgumentException(String.valueOf(supplier.get())); } } + /** + * Ensures that the given reference is not null. Throws a NullPointerException if the reference is null. + * + * @param reference the object to check for nullity + * @param supplier a supplier providing the error message + * @param the type of the reference + * @return the non-null reference + */ public static T checkNotNull(T reference, Supplier supplier) { if (reference == null) { throw new NullPointerException(supplier.get()); @@ -42,14 +58,36 @@ public static T checkNotNull(T reference, Supplier supplier) { return reference; } + /** + * Validates that the size of the given collection matches the expected size. Throws an IllegalArgumentException if + * the sizes do not match. + * + * @param collection the collection to check + * @param expectedSize the expected size of the collection + */ public static void checkSize(Collection collection, int expectedSize) { checkSize(expectedSize, collection.size(), () -> collection.toString()); } + /** + * Validates that the size of the given array matches the expected size. Throws an IllegalArgumentException if the + * sizes do not match. + * + * @param objects the array to check + * @param expectedSize the expected size of the array + */ public static void checkSize(Object[] objects, int expectedSize) { checkSize(expectedSize, objects.length, () -> Arrays.toString(objects)); } + /** + * Validates that the size of a collection or array matches the expected size. Throws an IllegalArgumentException if + * the sizes do not match. + * + * @param expectedSize the expected size + * @param actualSize the actual size + * @param collectionContents a supplier providing the contents of the collection or array + */ private static void checkSize(int expectedSize, int actualSize, Supplier collectionContents) { if (actualSize != expectedSize) { throw new IllegalArgumentException("Expected collection to have size '" + expectedSize @@ -57,14 +95,39 @@ private static void checkSize(int expectedSize, int actualSize, Supplier } } + /** + * Validates that the size of the given collection is within the specified range. Throws an IllegalArgumentException + * if the size is outside the range. + * + * @param collection the collection to check + * @param minSize the minimum size + * @param maxSize the maximum size + */ public static void checkSizeRange(Collection collection, int minSize, int maxSize) { checkSizeRange(minSize, maxSize, collection.size(), () -> collection.toString()); } + /** + * Validates that the size of the given array is within the specified range. Throws an IllegalArgumentException if + * the size is outside the range. + * + * @param objects the array to check + * @param minSize the minimum size + * @param maxSize the maximum size + */ public static void checkSizeRange(Object[] objects, int minSize, int maxSize) { checkSizeRange(minSize, maxSize, objects.length, () -> Arrays.toString(objects)); } + /** + * Validates that the size of a collection or array is within the specified range. Throws an IllegalArgumentException + * if the size is outside the range. + * + * @param minSize the minimum size + * @param maxSize the maximum size + * @param actualSize the actual size + * @param collectionContents a supplier providing the contents of the collection or array + */ private static void checkSizeRange(int minSize, int maxSize, int actualSize, Supplier collectionContents) { if (actualSize < minSize || actualSize > maxSize) { throw new IllegalArgumentException( @@ -73,6 +136,13 @@ private static void checkSizeRange(int minSize, int maxSize, int actualSize, Sup } } + /** + * Validates that the given value is an instance of the specified class. Throws an IllegalArgumentException if the + * value is not an instance of the class. + * + * @param value the object to check + * @param aClass the class to check against + */ public static void checkClass(Object value, Class aClass) { SpecsCheck.checkArgument(aClass.isInstance(value), () -> "Expected value to be an instance of " + aClass + ", however it is a " + value.getClass()); diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsFactory.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsFactory.java index e1ba84ea..31f2c3e6 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsFactory.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsFactory.java @@ -1,11 +1,11 @@ -/** +/* * Copyright 2012 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -31,33 +31,35 @@ import java.util.Set; /** + * @deprecated This class was created when the code base was still using Java 5.0. With Java 7, the Diamond Operator and the + * Collections methods, this class should no longer be used. Consider using Guava classes in com.google.common.collect, such as Maps, + * Lists, etc. + * * Factory methods for common objects, such as the ones in Java Collections. - * + * *

* The purpose of theses methods is to avoid writing the generic type when creating a new class. - * + * *

* IMPORTANT: Instead of using this class, consider using Guava classes in com.google.common.collect, such as Maps, * Lists, etc. - * + * *

* PS.: This class was created when the code base was still using Java 5.0. With Java 7, the Diamond Operator and the * Collections methods, this class should no longer be used. - * + * * @author Joao Bispo - * */ +@Deprecated public class SpecsFactory { /** * Creates a list of the given class type, containing 'elements'. * - * @param listClass - * @param elements - * @return + * @param listClass the class type of the list elements + * @param elements the elements to be added to the list + * @return a list containing the given elements */ - // public static List asList(Class listClass, U... elements) { - // public static List asList(U... elements) { public static List asList(Class listClass, Object... elements) { List list = SpecsFactory.newArrayList(); @@ -73,35 +75,77 @@ public static List asList(Class listClass, Object... elements) { return list; } - // TODO: These classes are no longer needed after Java 8 + /** + * Creates a new ArrayList. + * + * @param the type of elements in the list + * @return a new ArrayList + */ public static List newArrayList() { return new ArrayList<>(); } + /** + * Creates a new ArrayList with the specified initial capacity. + * + * @param initialCapacity the initial capacity of the list + * @param the type of elements in the list + * @return a new ArrayList + */ public static List newArrayList(int initialCapacity) { return new ArrayList<>(initialCapacity); } + /** + * Creates a new ArrayList containing the elements of the specified collection. + * + * @param elements the collection whose elements are to be placed into the list + * @param the type of elements in the list + * @return a new ArrayList + */ public static List newArrayList(Collection elements) { return new ArrayList<>(elements); } + /** + * Creates a new LinkedList. + * + * @param the type of elements in the list + * @return a new LinkedList + */ public static List newLinkedList() { return new LinkedList<>(); } + /** + * Creates a new LinkedList containing the elements of the specified collection. + * + * @param elements the collection whose elements are to be placed into the list + * @param the type of elements in the list + * @return a new LinkedList + */ public static List newLinkedList(Collection elements) { return new LinkedList<>(elements); } + /** + * Creates a new HashMap. + * + * @param the type of keys in the map + * @param the type of values in the map + * @return a new HashMap + */ public static Map newHashMap() { return new HashMap<>(); } /** - * @param map - * if null, uses empty map - * @return + * Creates a new HashMap containing the mappings of the specified map. + * + * @param map the map whose mappings are to be placed into the new map + * @param the type of keys in the map + * @param the type of values in the map + * @return a new HashMap */ public static Map newHashMap(Map map) { if (map == null) { @@ -111,34 +155,89 @@ public static Map newHashMap(Map map) { return new HashMap<>(map); } + /** + * Creates a new LinkedHashMap. + * + * @param the type of keys in the map + * @param the type of values in the map + * @return a new LinkedHashMap + */ public static Map newLinkedHashMap() { return new LinkedHashMap<>(); } + /** + * Creates a new EnumMap for the specified key class. + * + * @param keyClass the class of the keys in the map + * @param the type of keys in the map + * @param the type of values in the map + * @return a new EnumMap + */ public static , V> Map newEnumMap(Class keyClass) { return new EnumMap<>(keyClass); } + /** + * Creates a new HashSet containing the elements of the specified collection. + * + * @param elements the collection whose elements are to be placed into the set + * @param the type of elements in the set + * @return a new HashSet + */ public static Set newHashSet(Collection elements) { return new HashSet<>(elements); } + /** + * Creates a new HashSet. + * + * @param the type of elements in the set + * @return a new HashSet + */ public static Set newHashSet() { return new HashSet<>(); } + /** + * Creates a new LinkedHashMap containing the mappings of the specified map. + * + * @param elements the map whose mappings are to be placed into the new map + * @param the type of keys in the map + * @param the type of values in the map + * @return a new LinkedHashMap + */ public static Map newLinkedHashMap(Map elements) { return new LinkedHashMap<>(elements); } + /** + * Creates a new LinkedHashSet. + * + * @param the type of elements in the set + * @return a new LinkedHashSet + */ public static Set newLinkedHashSet() { return new LinkedHashSet<>(); } + /** + * Creates a new LinkedHashSet containing the elements of the specified collection. + * + * @param elements the collection whose elements are to be placed into the set + * @param the type of elements in the set + * @return a new LinkedHashSet + */ public static Set newLinkedHashSet(Collection elements) { return new LinkedHashSet<>(elements); } + /** + * Returns an InputStream for the specified file. + * + * @param file the file to be read + * @return an InputStream for the file, or null if the file is not found + */ public static InputStream getStream(File file) { try { InputStream stream = new FileInputStream(file); @@ -150,10 +249,12 @@ public static InputStream getStream(File file) { } /** - * Returns an empty map if the given map is null + * Returns an empty map if the given map is null. * - * @param map - * @return + * @param map the map to be checked + * @param the type of keys in the map + * @param the type of values in the map + * @return the original map, or an empty map if the original map is null */ public static Map assignMap(Map map) { @@ -167,9 +268,9 @@ public static Map assignMap(Map map) { /** * Builds a set with a sequence of integers starting at 'startIndex' and with 'size' integers. * - * @param i - * @param size - * @return + * @param startIndex the starting index of the sequence + * @param size the number of integers in the sequence + * @return a set containing the sequence of integers */ public static Set newSetSequence(int startIndex, int size) { Set set = SpecsFactory.newHashSet(); @@ -184,9 +285,8 @@ public static Set newSetSequence(int startIndex, int size) { /** * Converts an array of int to a List of Integer. * - * @param array - * - the original int array ( int[] ) - * @return a {@link List}<{@link Integer}> + * @param array the original int array + * @return a List of Integer */ public static List fromIntArray(int[] array) { @@ -205,8 +305,9 @@ public static List fromIntArray(int[] array) { *

* This method is useful for final fields whose contents do not need to be changed. * - * @param aList - * @return + * @param aList the list to be checked + * @param the type of elements in the list + * @return an unmodifiable view of the list, or an empty list if the original list is null or empty */ public static List getUnmodifiableList(List aList) { if (aList == null) { @@ -225,8 +326,9 @@ public static List getUnmodifiableList(List aList) { *

* If the source argument is null, the collection sink remains unmodified. * - * @param sink - * @param source + * @param sink the collection to which elements are to be added + * @param source the collection whose elements are to be added to the sink + * @param the type of elements in the collections */ public static void addAll(Collection sink, Collection source) { if (source == null) { @@ -236,21 +338,4 @@ public static void addAll(Collection sink, Collection source // Add all elements in source sink.addAll(source); } - - /** - * Parses the given list and returns an unmodifiable view of the list. - * - * @param aList - * @return - */ - /* - public static List getUnmodifiableList(List aList) { - List parsedList = parseList(aList); - if (parsedList.isEmpty()) { - return parsedList; - } - - return Collections.unmodifiableList(aList); - } - */ } diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsIo.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsIo.java index bd00f427..0e4e8fde 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsIo.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsIo.java @@ -73,7 +73,10 @@ import pt.up.fe.specs.util.utilities.ProgressCounter; /** - * Methods for quick and simple manipulation of files, folders and other input/output related operations. + * Utility methods for input/output operations. + *

+ * Provides static helper methods for reading, writing, and managing files and resources. + *

* * @author Joao Bispo */ diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsMath.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsMath.java index 666c327d..32fba8f1 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsMath.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsMath.java @@ -1,11 +1,11 @@ /* * Copyright 2011 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -17,18 +17,32 @@ import java.util.List; /** - * Utility methods with common mathematical operations. + * Utility methods for mathematical operations. + *

+ * Provides static helper methods for arithmetic, rounding, and other math-related tasks. + *

* * @author Joao Bispo */ public class SpecsMath { - // public static double zeroRatio(List values) { + /** + * Calculates the ratio of zero values in a collection of numbers. + * + * @param values the collection of numbers + * @return the ratio of zero values + */ public static double zeroRatio(Collection values) { return zeroRatio(values, 0.0); } - // public static double zeroRatio(List values, double threshold) { + /** + * Calculates the ratio of values below a given threshold in a collection of numbers. + * + * @param values the collection of numbers + * @param threshold the threshold value + * @return the ratio of values below the threshold + */ public static double zeroRatio(Collection values, double threshold) { double numZeros = 0; @@ -41,7 +55,12 @@ public static double zeroRatio(Collection values, double threshold) { return numZeros / values.size(); } - // public static double arithmeticMean(List values) { + /** + * Calculates the arithmetic mean of a collection of numbers. + * + * @param values the collection of numbers + * @return the arithmetic mean + */ public static double arithmeticMean(Collection values) { if (values.isEmpty()) { return 0; @@ -58,7 +77,12 @@ public static double arithmeticMean(Collection values) { return result; } - // public static double arithmeticMeanWithoutZeros(List values) { + /** + * Calculates the arithmetic mean of a collection of numbers, excluding zero values. + * + * @param values the collection of numbers + * @return the arithmetic mean excluding zeros, or null if the collection is empty + */ public static Double arithmeticMeanWithoutZeros(Collection values) { if (values.isEmpty()) { return null; @@ -81,74 +105,17 @@ public static Double arithmeticMeanWithoutZeros(Collection values) { return 0d; } - // result /= values.size(); result /= numElements; return result; } - // public static double geometricMean(List values) { - /* - public static double geometricMean(Collection values) { - double result = 1; - - //int zeros = 0; - for(Number value : values) { - - if (!(value.doubleValue() > 0.0)) { - //zeros++; - //continue; - //value = 0.000000001; - value = Double.MIN_VALUE; - } - //System.out.println("Value:"+value); - result *= value.doubleValue(); - } - //System.out.println("Piatorio:"+result); - - int numberOfElements = values.size(); - - double power = (double)1 / (double)numberOfElements; - //double power = (double)1 / (double)values.size(); - result = Math.pow(result, power); - //System.out.println("Final result:"+result); - - return result; - } - */ - // public static double geometricMeanWithZeroCorrection(List values) { - /* - public static double geometricMeanWithZeroCorrection(Collection values) { - double result = 1; - - int zeros = 0; - for(Number value : values) { - if (!(value.doubleValue() > 0.0)) { - zeros++; - continue; - } - //System.out.println("Value:"+value); - result *= value.doubleValue(); - } - //System.out.println("Piatorio:"+result); - - int numberOfElements = values.size() - zeros; - - double power = (double)1 / (double)numberOfElements; - //double power = (double)1 / (double)values.size(); - result = Math.pow(result, power); - //System.out.println("Final result:"+result); - - return result; - } - */ - // public static double geometricMeanWithZeroCorrection(List values) { /** - * - * @param values - * @param withoutZeros - * if false, performs geometric mean with zero correction. Otherwise, ignores the zero values. - * @return + * Calculates the geometric mean of a collection of numbers. + * + * @param values the collection of numbers + * @param withoutZeros if false, performs geometric mean with zero correction; otherwise, ignores zero values + * @return the geometric mean */ public static double geometricMean(Collection values, boolean withoutZeros) { double result = 1; @@ -159,10 +126,8 @@ public static double geometricMean(Collection values, boolean withoutZer zeros++; continue; } - // System.out.println("Value:"+value); result *= value.doubleValue(); } - // System.out.println("Piatorio:"+result); int numberOfElements; if (withoutZeros) { @@ -170,17 +135,20 @@ public static double geometricMean(Collection values, boolean withoutZer } else { numberOfElements = values.size(); } - // int numberOfElements = values.size() - zeros; double power = (double) 1 / (double) numberOfElements; - // double power = (double)1 / (double)values.size(); result = Math.pow(result, power); - // System.out.println("Final result:"+result); return result; } - // public static double harmonicMean(List values, boolean useZeroCorrection) { + /** + * Calculates the harmonic mean of a collection of numbers. + * + * @param values the collection of numbers + * @param useZeroCorrection if true, applies zero correction to the harmonic mean calculation + * @return the harmonic mean + */ public static double harmonicMean(Collection values, boolean useZeroCorrection) { double result = 0; int zeros = 0; @@ -188,10 +156,7 @@ public static double harmonicMean(Collection values, boolean useZeroCorr if (!(value.doubleValue() > 0.0) && !(value.doubleValue() < 0.0)) { zeros++; continue; - // value = 0.000000001; - // value = Double.MIN_VALUE; } - // System.out.println("Value:"+value); result += 1 / value.doubleValue(); } @@ -200,52 +165,29 @@ public static double harmonicMean(Collection values, boolean useZeroCorr if (numberOfElements == 0) { return 0.0; } - // int numberOfElements = values.size(); - // result = (double)values.size() / result; result = numberOfElements / result; - // Zero value correction - // System.out.println("Number of zeros:"+zeros); - // System.out.println("BEfore correction:"+result); if (useZeroCorrection) { result *= (double) numberOfElements / (double) values.size(); } - // System.out.println("AFter correction:"+result); - // result = (double)values.size() / result; - return result; - } - /* - public static double harmonicMeanWithZeroCorrection(List values) { - double result = 0; - int zeros = 0; - for(Number value : values) { - if(!(value.doubleValue() > 0.0)) { - zeros++; - continue; - } - //System.out.println("Value:"+value); - result += (double)1 / value.doubleValue(); - } - - int numberOfElements = values.size() - zeros; - //int numberOfElements = values.size(); - - result = (double)numberOfElements / result; - //result = (double)values.size() / result; - return result; + return result; } - */ + /** + * Finds the maximum value in a list of numbers. + * + * @param values the list of numbers + * @param ignoreZeros if true, ignores zero values in the calculation + * @return the maximum value, or null if the list is null or empty + */ public static Number max(List values, boolean ignoreZeros) { if (values == null) { - // return 0; return null; } if (values.isEmpty()) { - // return 0; return null; } @@ -270,14 +212,19 @@ public static Number max(List values, boolean ignoreZeros) { return max; } + /** + * Finds the minimum value in a list of numbers. + * + * @param values the list of numbers + * @param ignoreZeros if true, ignores zero values in the calculation + * @return the minimum value, or null if the list is null or empty + */ public static Number min(List values, boolean ignoreZeros) { if (values == null) { - // return 0; return null; } if (values.isEmpty()) { - // return 0; return null; } @@ -302,13 +249,12 @@ public static Number min(List values, boolean ignoreZeros) { return min; } - /* - public static Number sum(List graphOperationsPerIt) { - graphOperationsPerIt.get(0). - } - * + /** + * Calculates the sum of a list of numbers. + * + * @param numbers the list of numbers + * @return the sum of the numbers */ - public static double sum(List numbers) { double acc = 0; for (Number number : numbers) { @@ -318,6 +264,12 @@ public static double sum(List numbers) { return acc; } + /** + * Calculates the product of a list of numbers. + * + * @param numbers the list of numbers + * @return the product of the numbers + */ public static double multiply(List numbers) { double acc = 1; for (Number number : numbers) { @@ -328,10 +280,10 @@ public static double multiply(List numbers) { } /** - * Taken from here: https://stackoverflow.com/a/7879559/1189808 - * - * @param number - * @return + * Calculates the factorial of a number. + * + * @param number the number + * @return the factorial of the number */ public static long factorial(int number) { long result = 1; @@ -339,7 +291,7 @@ public static long factorial(int number) { for (int factor = 2; factor <= number; factor++) { result *= factor; } - // System.out.println("RESULT: " + result); + return result; } } diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsNumbers.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsNumbers.java index e0916026..36b5bce9 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsNumbers.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsNumbers.java @@ -1,11 +1,11 @@ -/** - * Copyright 2019 SPeCS. - * +/* + * Copyright 2019 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -17,13 +17,18 @@ import pt.up.fe.specs.util.classmap.ClassMap; /** - * Utility classes related with numbers. - * - * @author JoaoBispo + * Utility methods for number operations. + *

+ * Provides static helper methods for parsing, formatting, and converting numbers. + *

* + * @author Joao Bispo */ public class SpecsNumbers { + /** + * A map of number classes to their zero values. + */ private static final ClassMap ZEROS; static { ZEROS = new ClassMap<>(); @@ -33,6 +38,9 @@ public class SpecsNumbers { ZEROS.put(Double.class, 0.0); } + /** + * A map of number classes to their addition operations. + */ private static final BiFunctionClassMap ADD; static { ADD = new BiFunctionClassMap<>(); @@ -42,10 +50,24 @@ public class SpecsNumbers { ADD.put(Double.class, (number1, number2) -> Double.valueOf(number1.doubleValue() + number2.doubleValue())); } + /** + * Returns the zero value for the given number class. + * + * @param numberClass the class of the number + * @return the zero value for the given number class + */ public static Number zero(Class numberClass) { return ZEROS.get(numberClass); } + /** + * Adds two numbers of the same type. + * + * @param the type of the numbers + * @param number1 the first number + * @param number2 the second number + * @return the result of adding the two numbers + */ @SuppressWarnings("unchecked") // Functions should return correct number public static N add(N number1, N number2) { return (N) ADD.apply(number1, number2); diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsSwing.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsSwing.java index 7378615c..d18c4590 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsSwing.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsSwing.java @@ -1,11 +1,11 @@ /* * Copyright 2011 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -39,39 +39,57 @@ import pt.up.fe.specs.util.swing.MapModel; /** - * Utility methods related to Java GUI operation. - * + * Utility methods for Java Swing operations. + *

+ * Provides static helper methods for dialogs, UI helpers, and event handling in Java Swing applications. + *

+ * * @author Joao Bispo */ public class SpecsSwing { + /** + * The class name used to test if Swing is available in the current environment. + */ public static final String TEST_CLASSNAME = "javax.swing.JFrame"; + /** + * Custom Look and Feel class name, if set. + */ private static String CUSTOM_LOOK_AND_FEEL = null; + /** + * Sets a custom Look and Feel class name. + * + * @param value the class name of the custom Look and Feel + */ synchronized public static void setCustomLookAndFeel(String value) { CUSTOM_LOOK_AND_FEEL = value; } + /** + * Gets the custom Look and Feel class name, if set. + * + * @return the class name of the custom Look and Feel, or null if not set + */ synchronized public static String getCustomLookAndFeel() { return CUSTOM_LOOK_AND_FEEL; } - // public static boolean hasCustomLookAndFeel() { - // // Since it is a boolean, it should not have problems regarding reading concurrently, according to the Java - // // memory model - // return HAS_CUSTOM_LOOK_AND_FEEL; - // } - /** * Returns true if the Java package Swing is available. - * - * @return + * + * @return true if Swing is available, false otherwise */ public static boolean isSwingAvailable() { return SpecsSystem.isAvailable(SpecsSwing.TEST_CLASSNAME); } + /** + * Runs the given Runnable on the Swing Event Dispatch Thread. + * + * @param r the Runnable to execute + */ public static void runOnSwing(Runnable r) { if (SwingUtilities.isEventDispatchThread()) { r.run(); @@ -81,8 +99,8 @@ public static void runOnSwing(Runnable r) { } /** - * Sets the system Look&Feel for Swing components. - * + * Sets the system Look and Feel for Swing components. + * * @return true if no problem occurred, false otherwise */ public static boolean setSystemLookAndFeel() { @@ -100,8 +118,6 @@ public static boolean setSystemLookAndFeel() { // Set System L&F UIManager.setLookAndFeel(lookAndFeel); - // "com.sun.java.swing.plaf.motif.MotifLookAndFeel"); - // "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); return true; } catch (Exception e) { SpecsLogs.warn("Could not set system Look&Feel", e); @@ -110,13 +126,13 @@ public static boolean setSystemLookAndFeel() { return false; } + /** + * Gets the system Look and Feel class name, avoiding problematic defaults like Metal and GTK+. + * + * @return the class name of the system Look and Feel + */ public static String getSystemLookAndFeel() { - // Temporarily disable custom system look and feel - // if (true) { - // return UIManager.getSystemLookAndFeelClassName(); - // } - // Get custom L&F String customLookAndFeel = getCustomLookAndFeel(); if (customLookAndFeel != null) { @@ -139,33 +155,8 @@ public static String getSystemLookAndFeel() { return systemLookAndFeel; } - // // ... unless it is the only one available - // if (UIManager.getInstalledLookAndFeels().length == 1) { - // return systemLookAndFeel; - // } - - // SpecsLogs.debug("Default system look and feel is Metal, trying to use another one"); - // Map lookAndFeels = Arrays.stream(UIManager.getInstalledLookAndFeels()) - // .collect(Collectors.toMap(info -> info.getName(), info -> info.getClassName())); - - // // Build look and feels map - // Map lookAndFeels = new LinkedHashMap<>(); - // - // for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { - // lookAndFeels.put(info.getName(), info.getClassName()); - // } - // SpecsLogs.debug("Available look and feels: " + lookAndFeels); - - // Check if GTK+ is available - // String gtkLookAndFeel = lookAndFeels.get("GTK+"); - // if (gtkLookAndFeel != null) { - // return gtkLookAndFeel; - // } - String alternativeLookAndFeel = lookAndFeels.values().stream() - // Return first that is not Metal .filter(lookAndFeel -> !lookAndFeel.endsWith(".MetalLookAndFeel")) - // Recently, GTK+ on Linux is really buggy, avoid it too .filter(lookAndFeel -> !lookAndFeel.endsWith(".GTKLookAndFeel")) .findFirst().orElse(systemLookAndFeel); @@ -177,23 +168,18 @@ public static String getSystemLookAndFeel() { } /** - * Builds TableModels from Maps. - * - * @param map - * @param maxElementsPerTable - * @param rowWise - * @return + * Builds TableModels from Maps, splitting into multiple tables if necessary. + * + * @param map the map to convert into TableModels + * @param maxElementsPerTable the maximum number of elements per table + * @param rowWise whether the table should be row-wise + * @param valueClass the class of the values in the map + * @return a list of TableModels */ public static , V> List getTables(Map map, int maxElementsPerTable, boolean rowWise, Class valueClass) { List tableModels = new ArrayList<>(); - // K and V will be rows - // int numMaps = (int) Math.ceil((double)map.size() / (double)maxCols); - // int rowCount = 2; - // int mapCols = 0; - - // int currentCols = map.size(); List keys = new ArrayList<>(); keys.addAll(map.keySet()); Collections.sort(keys); @@ -229,18 +215,16 @@ public static , V> List getTables(Ma } /** - * Builds TableModels from Maps. - * - * @param map - * @param maxElementsPerTable - * @param rowWise - * @return + * Builds a single TableModel from a Map. + * + * @param map the map to convert into a TableModel + * @param rowWise whether the table should be row-wise + * @param valueClass the class of the values in the map + * @return a TableModel */ public static , V> TableModel getTable(Map map, boolean rowWise, Class valueClass) { - // K and V will be rows - List keys = new ArrayList<>(); keys.addAll(map.keySet()); Collections.sort(keys); @@ -251,37 +235,39 @@ public static , V> TableModel getTable(Map } // Build map - // Map newMap = new HashMap(); Map newMap = SpecsFactory.newLinkedHashMap(); for (K key : currentKeys) { newMap.put(key, map.get(key)); } return new MapModel<>(newMap, rowWise, valueClass); - } + /** + * Displays a JPanel in a JFrame with the given title. + * + * @param panel the JPanel to display + * @param title the title of the JFrame + * @return the JFrame containing the panel + */ public static JFrame showPanel(JPanel panel, String title) { return showPanel(panel, title, 0, 0); } /** - * Launches the given panel in a JFrame. - * - * @param panel - * @param title - * @param x - * @param y - * @return + * Creates a new JFrame containing the given JPanel. + * + * @param panel the JPanel to display + * @param title the title of the JFrame + * @param x the x-coordinate of the JFrame + * @param y the y-coordinate of the JFrame + * @return the JFrame containing the panel */ public static JFrame newWindow(JPanel panel, String title, int x, int y) { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - // frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - frame.setResizable(true); - frame.setLocation(x, y); // Add content to the window. @@ -291,22 +277,18 @@ public static JFrame newWindow(JPanel panel, String title, int x, int y) { return frame; } + /** + * Displays a JPanel in a JFrame with the given title and location. + * + * @param panel the JPanel to display + * @param title the title of the JFrame + * @param x the x-coordinate of the JFrame + * @param y the y-coordinate of the JFrame + * @return the JFrame containing the panel + */ public static JFrame showPanel(JPanel panel, String title, int x, int y) { final JFrame frame = newWindow(panel, title, x, y); - /* - JFrame frame = new JFrame(); - - // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - - frame.setResizable(true); - - frame.setLocation(x, y); - - // Add content to the window. - frame.add(panel, BorderLayout.CENTER); - frame.setTitle(title); - */ + SpecsSwing.runOnSwing(() -> { frame.pack(); frame.setVisible(true); @@ -317,9 +299,9 @@ public static JFrame showPanel(JPanel panel, String title, int x, int y) { } /** - * Taken from here: https://stackoverflow.com/a/16611566 - * - * @return true if no screen is available for displaying Swing components, false otherwise + * Checks if the current environment is headless, i.e., no screen is available for displaying Swing components. + * + * @return true if the environment is headless, false otherwise */ public static boolean isHeadless() { if (GraphicsEnvironment.isHeadless()) { @@ -336,6 +318,9 @@ public static boolean isHeadless() { /** * Opens a folder containing the file and selects it in a default system file manager. + * + * @param file the file to select + * @return true if the operation was successful, false otherwise */ public static boolean browseFileDirectory(File file) { if (!file.exists()) { @@ -353,7 +338,6 @@ public static boolean browseFileDirectory(File file) { return false; } return true; - // return; } if (SpecsSystem.isLinux()) { @@ -366,11 +350,9 @@ public static boolean browseFileDirectory(File file) { return false; } return true; - // return; } Desktop.getDesktop().browseFileDirectory(file); return true; - // return; } } diff --git a/SpecsUtils/src/pt/up/fe/specs/util/providers/FileResourceManager.java b/SpecsUtils/src/pt/up/fe/specs/util/providers/FileResourceManager.java index da641c46..292536fc 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/providers/FileResourceManager.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/providers/FileResourceManager.java @@ -1,11 +1,11 @@ -/** - * Copyright 2018 SPeCS. - * +/* + * Copyright 2018 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -25,13 +25,32 @@ import pt.up.fe.specs.util.SpecsLogs; import pt.up.fe.specs.util.properties.SpecsProperties; +/** + * Utility class for managing file resources. + *

+ * Provides methods for loading, caching, and accessing files. + *

+ */ public class FileResourceManager { + /** + * Map of available resources, keyed by their names. + */ private final Map availableResources; + + /** + * Map of local resources, keyed by their names. + */ private Map localResources; + /** + * Creates a FileResourceManager instance from an enum class. + * + * @param the type of the enum + * @param enumClass the class of the enum + * @return a new FileResourceManager instance + */ public static & Supplier> FileResourceManager fromEnum( - // Class enumClass, String localResourcesFilename) { Class enumClass) { Map availableResources = new LinkedHashMap<>(); @@ -39,29 +58,37 @@ public static & Supplier> FileResourceM availableResources.put(anEnum.name(), anEnum.get()); } - // return new FileResourceManager(availableResources, localResourcesFilename); return new FileResourceManager(availableResources); } - // public FileResourceManager(Map availableResources, String localResourcesFilename) { + /** + * Constructs a FileResourceManager with the given available resources. + * + * @param availableResources a map of available resources + */ public FileResourceManager(Map availableResources) { this.availableResources = availableResources; - // Populate local resources - // this.localResources = buildLocalResources(localResourcesFilename); + // Initialize local resources this.localResources = new HashMap<>(); - } - // public void setLocalResources(String localResourcesFilename) { - // this.localResources = buildLocalResources(localResourcesFilename); - // } - + /** + * Adds local resources from a specified file. + * + * @param localResourcesFilename the filename of the local resources file + */ public void addLocalResources(String localResourcesFilename) { Map resources = buildLocalResources(localResourcesFilename); this.localResources.putAll(resources); } + /** + * Builds a map of local resources from a specified file. + * + * @param localResourcesFilename the filename of the local resources file + * @return a map of local resources + */ private Map buildLocalResources(String localResourcesFilename) { // Check if there is a local resources file Optional localResourcesTry = SpecsIo.getLocalFile(localResourcesFilename, getClass()); @@ -104,10 +131,22 @@ private Map buildLocalResources(String localResourcesFilename) { return localResourcesMap; } + /** + * Retrieves a file resource provider for the given enum value. + * + * @param resourceEnum the enum value representing the resource + * @return the file resource provider + */ public FileResourceProvider get(Enum resourceEnum) { return get(resourceEnum.name()); } + /** + * Retrieves a file resource provider for the given resource name. + * + * @param resourceName the name of the resource + * @return the file resource provider + */ public FileResourceProvider get(String resourceName) { // 1. Check if there is a local resource for this resource File localResource = localResources.get(resourceName); diff --git a/SpecsUtils/src/pt/up/fe/specs/util/providers/FileResourceProvider.java b/SpecsUtils/src/pt/up/fe/specs/util/providers/FileResourceProvider.java index e6dda7af..e8767106 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/providers/FileResourceProvider.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/providers/FileResourceProvider.java @@ -1,11 +1,11 @@ -/** +/* * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -24,45 +24,83 @@ import pt.up.fe.specs.util.providers.impl.GenericFileResourceProvider; /** - * Provides a resource in the format of a file, that might or not exist yet. - * - * @author JoaoBispo + * Utility class for providing file resources. + *

+ * Used for loading and managing file-based resources. + *

* + * @author Joao Bispo */ public interface FileResourceProvider { + /** + * Creates a new instance of FileResourceProvider for an existing file. + * + * @param existingFile the file to be wrapped by the provider + * @return a new instance of FileResourceProvider + */ static FileResourceProvider newInstance(File existingFile) { return GenericFileResourceProvider.newInstance(existingFile); } + /** + * Creates a new instance of FileResourceProvider for an existing file with a version suffix. + * + * @param existingFile the file to be wrapped by the provider + * @param versionSuffix the version suffix to be appended + * @return a new instance of FileResourceProvider + */ static FileResourceProvider newInstance(File existingFile, String versionSuffix) { return GenericFileResourceProvider.newInstance(existingFile, versionSuffix); } /** * Helper class for versioned writing. - * - * @author JoaoBispo + *

+ * Contains information about the written file and whether it is a new file. + *

* + * @author Joao Bispo */ public static class ResourceWriteData { private final File writtenFile; private final boolean newFile; + /** + * Constructs a ResourceWriteData object. + * + * @param writtenFile the file that was written + * @param newFile whether the file is new + */ public ResourceWriteData(File writtenFile, boolean newFile) { Preconditions.checkNotNull(writtenFile, "writtenFile should not be null"); this.writtenFile = writtenFile; this.newFile = newFile; } + /** + * Gets the written file. + * + * @return the written file + */ public File getFile() { return writtenFile; } + /** + * Checks if the file is new. + * + * @return true if the file is new, false otherwise + */ public boolean isNewFile() { return newFile; } + /** + * Makes the file executable if it is new and the operating system is Linux. + * + * @param isLinux true if the operating system is Linux, false otherwise + */ public void makeExecutable(boolean isLinux) { // If file is new and we are in a flavor of Linux, make file executable if (isNewFile() && isLinux) { @@ -80,43 +118,52 @@ public void makeExecutable(boolean isLinux) { /** * Copies this resource to the given folder. - * - * @param folder - * @return + * + * @param folder the destination folder + * @return the file that was written */ File write(File folder); /** - * - * @return string representing the version of this resource + * Gets the version of this resource. + * + * @return a string representing the version of this resource */ String getVersion(); /** - * - * @return the name of the file represented by this resource + * Gets the name of the file represented by this resource. + * + * @return the name of the file */ String getFilename(); /** * Copies this resource to the destination folder. If the file already exists, uses method getVersion() to determine * if the file should be overwritten or not. - * *

* If the file already exists but no versioning information is available in the system, the file is overwritten. - * *

* The method will use the package of the class indicated in 'context' as the location to store the information * about versioning. Keep in mind that calls using the same context will refer to the same local copy of the * resource. - * - * @param folder - * @return + * + * @param folder the destination folder + * @param context the class used to store versioning information + * @return a ResourceWriteData object containing information about the written file */ default ResourceWriteData writeVersioned(File folder, Class context) { return writeVersioned(folder, context, true); } + /** + * Copies this resource to the destination folder with versioning information. + * + * @param folder the destination folder + * @param context the class used to store versioning information + * @param writeIfNoVersionInfo whether to write the file if no versioning information is available + * @return a ResourceWriteData object containing information about the written file + */ default ResourceWriteData writeVersioned(File folder, Class context, boolean writeIfNoVersionInfo) { // Create file // String resourceOutput = usePath ? getFilepath() : getFilename(); @@ -173,14 +220,14 @@ default ResourceWriteData writeVersioned(File folder, Class context, boolean /** * Creates a resource for the given version. - * *

* It changes the resource path by appending an underscore and the given version as a suffix, before any * extension.
* E.g., if the original resource is "path/executable.exe", returns a resource to "path/executable.exe". - * - * @param version - * @return + *

+ * + * @param version the version suffix to be appended + * @return a new FileResourceProvider for the given version */ default FileResourceProvider createResourceVersion(String version) { throw new NotImplementedException(getClass()); diff --git a/SpecsUtils/src/pt/up/fe/specs/util/providers/KeyEnumNameProvider.java b/SpecsUtils/src/pt/up/fe/specs/util/providers/KeyEnumNameProvider.java index 4be64c43..85be2dcd 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/providers/KeyEnumNameProvider.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/providers/KeyEnumNameProvider.java @@ -1,11 +1,11 @@ -/** - * Copyright 2016 SPeCS. - * +/* + * Copyright 2016 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -14,15 +14,30 @@ package pt.up.fe.specs.util.providers; /** - * Implementation of KeyStringProvider for enums where we want the keys to be Enum.name(). - * - * @author JoaoBispo + * Functional interface for providing enum names as keys. + *

+ * Used for supplying enum-based key values. + *

* + * @author Joao Bispo */ public interface KeyEnumNameProvider extends KeyStringProvider { + /** + * Returns the name of the enum constant. + * + * @return the name of the enum constant + */ String name(); + /** + * Provides the key associated with the enum constant. + *

+ * The key is the name of the enum constant. + *

+ * + * @return the key associated with the enum constant + */ @Override default String getKey() { return name(); diff --git a/SpecsUtils/src/pt/up/fe/specs/util/providers/KeyProvider.java b/SpecsUtils/src/pt/up/fe/specs/util/providers/KeyProvider.java index 56f87400..eb9bf103 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/providers/KeyProvider.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/providers/KeyProvider.java @@ -1,11 +1,11 @@ -/** +/* * Copyright 2012 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -14,17 +14,22 @@ package pt.up.fe.specs.util.providers; /** - * Represents a class which provides a key that can be used to identify it (for instance, in a Map). - * + * Functional interface for providing keys. + *

+ * Used for supplying key values. + *

+ * * @author Joao Bispo - * */ public interface KeyProvider { /** * The key corresponding to this instance. + *

+ * This method returns the key that uniquely identifies the instance. + *

* - * @return + * @return the key of type T */ T getKey(); } diff --git a/SpecsUtils/src/pt/up/fe/specs/util/providers/KeyStringProvider.java b/SpecsUtils/src/pt/up/fe/specs/util/providers/KeyStringProvider.java index c3ad6aaa..d5890dbf 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/providers/KeyStringProvider.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/providers/KeyStringProvider.java @@ -1,11 +1,11 @@ -/** +/* * Copyright 2013 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -19,16 +19,31 @@ import pt.up.fe.specs.util.SpecsFactory; /** - * A KeyProvider specialized to Strings. - * + * Functional interface for providing string keys. + *

+ * Used for supplying string-based key values. + *

+ * * @author Joao Bispo */ public interface KeyStringProvider extends KeyProvider { + /** + * Converts an array of KeyStringProvider instances into a list of strings. + * + * @param providers an array of KeyStringProvider instances + * @return a list of string keys provided by the instances + */ public static List toList(KeyStringProvider... providers) { return toList(Arrays.asList(providers)); } + /** + * Converts a list of KeyStringProvider instances into a list of strings. + * + * @param providers a list of KeyStringProvider instances + * @return a list of string keys provided by the instances + */ public static List toList(List providers) { List strings = SpecsFactory.newArrayList(); diff --git a/SpecsUtils/src/pt/up/fe/specs/util/providers/ProvidersSupport.java b/SpecsUtils/src/pt/up/fe/specs/util/providers/ProvidersSupport.java index 66abc17e..a4d50da3 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/providers/ProvidersSupport.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/providers/ProvidersSupport.java @@ -1,14 +1,14 @@ -/** - * Copyright 2017 SPeCS. - * +/* + * Copyright 2017 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.util.providers; @@ -19,12 +19,21 @@ import pt.up.fe.specs.util.SpecsFactory; /** - * Package support class. - * - * @author JoaoBispo + * Utility class for supporting provider interfaces. + *

+ * Provides helper methods for working with resource and key providers. + *

* + * @author Joao Bispo */ -class ProvidersSupport { +public class ProvidersSupport { + /** + * Retrieves a list of resources from a single enum class implementing the ResourceProvider interface. + * + * @param enumClass the class of the enum implementing ResourceProvider + * @return a list of resources provided by the enum + * @throws NullPointerException if the provided class is not an enum + */ static List getResourcesFromEnumSingle(Class enumClass) { ResourceProvider[] enums = enumClass.getEnumConstants(); diff --git a/SpecsUtils/src/pt/up/fe/specs/util/providers/ResourceProvider.java b/SpecsUtils/src/pt/up/fe/specs/util/providers/ResourceProvider.java index 8a8592fd..ce41fdf4 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/providers/ResourceProvider.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/providers/ResourceProvider.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2012 SPeCS Research Group. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with @@ -28,6 +28,11 @@ import pt.up.fe.specs.util.providers.impl.GenericResource; /** + * Functional interface for providing resources. + *

+ * Used for supplying resource objects. + *

+ * * Represents a class which provides a string to a Java resource. * *

@@ -40,14 +45,32 @@ @FunctionalInterface public interface ResourceProvider extends FileResourceProvider { + /** + * Creates a new instance of ResourceProvider with the given resource string. + * + * @param resource the resource string + * @return a new ResourceProvider instance + */ static ResourceProvider newInstance(String resource) { return () -> resource; } + /** + * Creates a new instance of ResourceProvider with the given resource string and version. + * + * @param resource the resource string + * @param version the version string + * @return a new ResourceProvider instance + */ static ResourceProvider newInstance(String resource, String version) { return new GenericResource(resource, version); } + /** + * Returns the default version string for resources. + * + * @return the default version string + */ static String getDefaultVersion() { return "1.0"; } @@ -58,14 +81,14 @@ static String getDefaultVersion() { *

* Resources are '/' separated, and must not end with a '/'. * - * @return + * @return the resource path string */ String getResource(); /** * Returns a list with all the resources, in case this class is an enum. Otherwise, returns an empty list. * - * @return + * @return a list of ResourceProvider instances */ default List getEnumResources() { ResourceProvider[] resourcesArray = getClass().getEnumConstants(); @@ -84,6 +107,12 @@ default List getEnumResources() { return resources; } + /** + * Retrieves resources from a list of classes that implement ResourceProvider. + * + * @param providers a list of classes implementing ResourceProvider + * @return a list of ResourceProvider instances + */ public static & ResourceProvider> List getResourcesFromEnum( List> providers) { @@ -96,6 +125,12 @@ public static & ResourceProvider> List getR return resources; } + /** + * Retrieves resources from an array of classes that implement ResourceProvider. + * + * @param enumClasses an array of classes implementing ResourceProvider + * @return a list of ResourceProvider instances + */ @SafeVarargs public static List getResourcesFromEnum(Class... enumClasses) { return getResourcesFromEnum(Arrays.asList(enumClasses)); @@ -104,8 +139,8 @@ public static List getResourcesFromEnum(Class & ResourceProvider> List getResources( Class enumClass) { @@ -122,8 +157,9 @@ public static & ResourceProvider> List getR } /** + * Returns the name of the last part of the resource, without the 'path'. * - * @return the name of the last part of resource, without the 'path' + * @return the resource name */ default String getResourceName() { String resourcePath = getResource(); @@ -137,9 +173,9 @@ default String getResourceName() { } /** - * Returns the location of the resource, i.e., the parent package/folder + * Returns the location of the resource, i.e., the parent package/folder. * - * @return + * @return the resource location */ default String getResourceLocation() { String resourcePath = getFileLocation(); @@ -156,28 +192,51 @@ default String getResourceLocation() { /** * Returns the path that should be used when copying this resource. By default returns the same as getResource(). * - * @return + * @return the file location path */ default String getFileLocation() { return getResource(); } + /** + * Writes the resource to the working directory. + * + * @return the written file + */ default File write() { return write(SpecsIo.getWorkingDir()); } /** * Helper method which by default overwrites the file. + * + * @param folder the folder where the resource will be written + * @return the written file */ @Override default File write(File folder) { return write(folder, true); } + /** + * Writes the resource to the specified folder, with an option to overwrite. + * + * @param folder the folder where the resource will be written + * @param overwrite whether to overwrite the file if it exists + * @return the written file + */ default File write(File folder, boolean overwrite) { return write(folder, overwrite, resourceName -> resourceName); } + /** + * Writes the resource to the specified folder, with options to overwrite and map the resource name. + * + * @param folder the folder where the resource will be written + * @param overwrite whether to overwrite the file if it exists + * @param nameMapper a function to map the resource name + * @return the written file + */ default File write(File folder, boolean overwrite, Function nameMapper) { Preconditions.checkArgument(folder.isDirectory(), folder + " does not exist"); @@ -202,27 +261,39 @@ default File write(File folder, boolean overwrite, Function name } /** - * - * @return the contents of this resource + * Reads the contents of this resource. + * + * @return the resource contents */ default String read() { return SpecsIo.getResource(this); } /** + * Returns the version of this resource. * - * @return string representing the version of this resource + * @return the version string */ @Override default String getVersion() { return getDefaultVersion(); } + /** + * Returns the filename of this resource. + * + * @return the filename string + */ @Override default String getFilename() { return getResourceName(); } + /** + * Converts this resource to an InputStream. + * + * @return the InputStream of the resource + */ default InputStream toStream() { return SpecsIo.resourceToStream(this); } diff --git a/SpecsUtils/src/pt/up/fe/specs/util/providers/Resources.java b/SpecsUtils/src/pt/up/fe/specs/util/providers/Resources.java index 987d4967..7be71475 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/providers/Resources.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/providers/Resources.java @@ -1,11 +1,11 @@ -/** - * Copyright 2016 SPeCS. - * +/* + * Copyright 2016 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -18,25 +18,51 @@ import java.util.stream.Collectors; /** - * Helper class to manage resources. - * - * @author JoaoBispo + * Utility class for managing and accessing resources. + *

+ * Provides methods for loading, caching, and retrieving resources. + *

* + * @author Joao Bispo */ public class Resources { + /** + * The base folder where resources are located. + */ private final String baseFolder; + + /** + * A list of resource names. + */ private final List resources; + /** + * Constructs a Resources object with a base folder and an array of resource names. + * + * @param baseFolder the base folder where resources are located + * @param resources an array of resource names + */ public Resources(String baseFolder, String... resources) { this(baseFolder, Arrays.asList(resources)); } + /** + * Constructs a Resources object with a base folder and a list of resource names. + * + * @param baseFolder the base folder where resources are located + * @param resources a list of resource names + */ public Resources(String baseFolder, List resources) { this.baseFolder = baseFolder.endsWith("/") ? baseFolder : baseFolder + "/"; this.resources = resources; } + /** + * Retrieves a list of ResourceProvider objects corresponding to the resources. + * + * @return a list of ResourceProvider objects + */ public List getResources() { return resources.stream() .map(resource -> baseFolder + resource) diff --git a/SpecsUtils/src/pt/up/fe/specs/util/providers/StringProvider.java b/SpecsUtils/src/pt/up/fe/specs/util/providers/StringProvider.java index 0452c4da..8734b2e7 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/providers/StringProvider.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/providers/StringProvider.java @@ -1,11 +1,11 @@ -/** +/* * Copyright 2015 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -19,24 +19,27 @@ import pt.up.fe.specs.util.providers.impl.CachedStringProvider; /** - * Returns a String. - * + * Functional interface for providing strings. *

- * Can be used to pass a lazy-initialized String that is potentially expensive (i.e., the contents of a File) and not - * always used. - * - * @author JoaoBispo + * Used for supplying string resources or values. + *

* + * @author Joao Bispo */ public interface StringProvider extends KeyProvider { /** - * + * Returns the string provided by this StringProvider. * * @return a string */ String getString(); + /** + * Returns the key associated with this StringProvider, which is the string itself. + * + * @return the key + */ @Override default String getKey() { return getString(); @@ -45,17 +48,29 @@ default String getKey() { /** * Creates a new StringProvider backed by the given String. * - * @param string - * @return + * @param string the string to be provided + * @return a new StringProvider instance */ static StringProvider newInstance(String string) { return () -> string; } + /** + * Creates a new StringProvider backed by the contents of the given File. + * + * @param file the file whose contents will be provided + * @return a new StringProvider instance + */ static StringProvider newInstance(File file) { return new CachedStringProvider(() -> SpecsIo.read(file)); } + /** + * Creates a new StringProvider backed by the contents of the given ResourceProvider. + * + * @param resource the resource whose contents will be provided + * @return a new StringProvider instance + */ static StringProvider newInstance(ResourceProvider resource) { return new CachedStringProvider(() -> SpecsIo.getResource(resource)); } diff --git a/SpecsUtils/src/pt/up/fe/specs/util/providers/WebResourceProvider.java b/SpecsUtils/src/pt/up/fe/specs/util/providers/WebResourceProvider.java index 0654ede2..088c87fc 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/providers/WebResourceProvider.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/providers/WebResourceProvider.java @@ -1,11 +1,11 @@ -/** +/* * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -22,39 +22,79 @@ import pt.up.fe.specs.util.providers.impl.GenericWebResourceProvider; /** - * Provides an URL to a web resource. - * - * @author JoaoBispo + * Functional interface for providing web resources. + *

+ * Used for supplying web-based resources. + *

* + * @author Joao Bispo */ public interface WebResourceProvider extends FileResourceProvider { + /** + * Creates a new instance of WebResourceProvider with the given root URL and resource URL. + * + * @param rootUrl the root URL of the web resource + * @param resourceUrl the specific resource URL + * @return a new WebResourceProvider instance + */ static WebResourceProvider newInstance(String rootUrl, String resourceUrl) { return new GenericWebResourceProvider(rootUrl, resourceUrl, "1.0"); } + /** + * Creates a new instance of WebResourceProvider with the given root URL, resource URL, and version. + * + * @param rootUrl the root URL of the web resource + * @param resourceUrl the specific resource URL + * @param version the version of the resource + * @return a new WebResourceProvider instance + */ static WebResourceProvider newInstance(String rootUrl, String resourceUrl, String version) { return new GenericWebResourceProvider(rootUrl, resourceUrl, version); } + /** + * Gets the specific resource URL. + * + * @return the resource URL + */ String getResourceUrl(); + /** + * Gets the root URL of the web resource. + * + * @return the root URL + */ String getRootUrl(); /** - * - * @return The string corresponding to an url + * Constructs the full URL string using the root URL. + * + * @return the full URL string */ default String getUrlString() { return getUrlString(getRootUrl()); } + /** + * Constructs the full URL string using the given root URL. + * + * @param rootUrl the root URL to use + * @return the full URL string + */ default String getUrlString(String rootUrl) { String sanitizedRootUrl = rootUrl.endsWith("/") ? rootUrl : rootUrl + "/"; return sanitizedRootUrl + getResourceUrl(); } + /** + * Converts the URL string to a URL object. + * + * @return the URL object + * @throws RuntimeException if the URL string cannot be converted + */ default URL getUrl() { try { return new URL(getUrlString()); @@ -64,8 +104,9 @@ default URL getUrl() { } /** - * - * @return string representing the version of this resource + * Gets the version of the web resource. + * + * @return the version string */ @Override default String getVersion() { @@ -73,8 +114,9 @@ default String getVersion() { } /** - * - * @return the name of the last part of the URL, without the 'path' + * Gets the filename of the web resource, which is the last part of the URL without the path. + * + * @return the filename */ @Override default String getFilename() { @@ -90,7 +132,11 @@ default String getFilename() { } /** + * Downloads the web resource to the specified folder. * Ignores usePath, always writes the file to the destination folder. + * + * @param folder the destination folder + * @return the downloaded file */ @Override default File write(File folder) { @@ -103,14 +149,14 @@ default File write(File folder) { /** * Creates a resource for the given version. - * *

* It changes the resource path by appending an underscore and the given version as a suffix, before any * extension.
* E.g., if the original resource is "path/executable.exe", returns a resource to "path/executable.exe". - * - * @param version - * @return + *

+ * + * @param version the version to append to the resource path + * @return a new WebResourceProvider instance for the given version */ @Override default WebResourceProvider createResourceVersion(String version) { diff --git a/SpecsUtils/src/pt/up/fe/specs/util/reporting/DefaultMessageType.java b/SpecsUtils/src/pt/up/fe/specs/util/reporting/DefaultMessageType.java index a11154ca..1291a5f3 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/reporting/DefaultMessageType.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/reporting/DefaultMessageType.java @@ -1,11 +1,11 @@ -/** +/* * Copyright 2015 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -13,21 +13,50 @@ package pt.up.fe.specs.util.reporting; +/** + * Class for default message types in reporting. + *

+ * Used for standardizing message types in the SPeCS ecosystem. + *

+ */ class DefaultMessageType implements MessageType { + /** + * The name of the message type. + */ private final String name; + + /** + * The category of the message type. + */ private final ReportCategory category; + /** + * Constructs a DefaultMessageType with the given name and category. + * + * @param name the name of the message type + * @param category the category of the message type + */ public DefaultMessageType(String name, ReportCategory category) { this.name = name; this.category = category; } + /** + * Gets the name of the message type. + * + * @return the name of the message type + */ @Override public String getName() { return this.name; } + /** + * Gets the category of the message type. + * + * @return the category of the message type + */ @Override public ReportCategory getMessageCategory() { return this.category; diff --git a/SpecsUtils/src/pt/up/fe/specs/util/reporting/MessageType.java b/SpecsUtils/src/pt/up/fe/specs/util/reporting/MessageType.java index 8e8ce343..60511a68 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/reporting/MessageType.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/reporting/MessageType.java @@ -1,11 +1,11 @@ -/** +/* * Copyright 2015 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -14,16 +14,30 @@ package pt.up.fe.specs.util.reporting; /** - * Specifies a type of warning or error. - * + * Interface for defining types of messages in reporting. + *

+ * Used for categorizing and handling different message types. + *

+ * * @author Luís Reis * @see Reporter */ public interface MessageType { + /** + * Returns the name of the message type. + * By default, it returns the string representation of the message type. + * + * @return the name of the message type + */ public default String getName() { return toString(); } + /** + * Returns the category of the message type. + * + * @return the category of the message type + */ public ReportCategory getMessageCategory(); /** diff --git a/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReportCategory.java b/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReportCategory.java index 9bb627c2..575c0f92 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReportCategory.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReportCategory.java @@ -1,11 +1,11 @@ -/** +/* * Copyright 2015 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -13,8 +13,25 @@ package pt.up.fe.specs.util.reporting; +/** + * Enum for categorizing report messages. + *

+ * Used for organizing and filtering reports. + *

+ */ public enum ReportCategory { + /** + * Represents an error message. + */ ERROR, + + /** + * Represents a warning message. + */ WARNING, + + /** + * Represents an informational message. + */ INFORMATION } diff --git a/SpecsUtils/src/pt/up/fe/specs/util/reporting/Reporter.java b/SpecsUtils/src/pt/up/fe/specs/util/reporting/Reporter.java index 933745a3..8d3c7da1 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/reporting/Reporter.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/reporting/Reporter.java @@ -1,11 +1,11 @@ -/** - * Copyright 2015 SPeCS. - * +/* + * Copyright 2015 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -18,10 +18,12 @@ import pt.up.fe.specs.util.Preconditions; /** - * An interface to unify the error/warning reporting system. - * - * @author Luís Reis + * Interface for reporting messages and events. + *

+ * Used for logging, error reporting, and user feedback. + *

* + * @author Luís Reis */ public interface Reporter { /** @@ -43,8 +45,19 @@ public interface Reporter { */ public void emitMessage(MessageType type, String message); + /** + * Prints the stack trace to the provided PrintStream. + * + * @param reportStream + * The stream where the stack trace will be printed. + */ public void printStackTrace(PrintStream reportStream); + /** + * Retrieves the PrintStream used for reporting. + * + * @return The PrintStream used for reporting. + */ public PrintStream getReportStream(); /** @@ -73,6 +86,7 @@ public default RuntimeException emitError(MessageType type, String message) { * Emits a default warning message. * * @param message + * The warning message to be emitted. */ public default void warn(String message) { emitMessage(MessageType.WARNING_TYPE, message); @@ -82,6 +96,7 @@ public default void warn(String message) { * Emits a default info message. * * @param message + * The info message to be emitted. */ public default void info(String message) { emitMessage(MessageType.INFO_TYPE, message); @@ -91,6 +106,8 @@ public default void info(String message) { * Emits a default error message. * * @param message + * The error message to be emitted. + * @return A RuntimeException containing the error message. */ public default RuntimeException error(String message) { return emitError(MessageType.ERROR_TYPE, message); diff --git a/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReporterUtils.java b/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReporterUtils.java index f388d10a..9b0398c5 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReporterUtils.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReporterUtils.java @@ -1,11 +1,11 @@ -/** +/* * Copyright 2015 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -15,10 +15,23 @@ import pt.up.fe.specs.util.Preconditions; +/** + * Utility methods for working with Reporter interfaces and reporting utilities. + *

+ * Provides static helper methods for managing and formatting reports. + *

+ */ public class ReporterUtils { private ReporterUtils() { } + /** + * Formats a message with a given type and content. + * + * @param messageType the type of the message (e.g., "Error", "Warning") + * @param message the content of the message + * @return a formatted message string + */ public static String formatMessage(String messageType, String message) { @@ -28,6 +41,14 @@ public static String formatMessage(String messageType, return messageType + ": " + message; } + /** + * Formats a stack line for a file, including the file name, line number, and code line. + * + * @param fileName the name of the file + * @param lineNumber the line number in the file + * @param codeLine the code line at the specified line number + * @return a formatted stack line string + */ public static String formatFileStackLine(String fileName, int lineNumber, String codeLine) { Preconditions.checkArgument(fileName != null); Preconditions.checkArgument(codeLine != null); @@ -35,6 +56,15 @@ public static String formatFileStackLine(String fileName, int lineNumber, String return "At " + fileName + ":" + lineNumber + ":\n > " + codeLine.trim(); } + /** + * Formats a stack line for a function, including the function name, file name, line number, and code line. + * + * @param functionName the name of the function + * @param fileName the name of the file + * @param lineNumber the line number in the file + * @param codeLine the code line at the specified line number + * @return a formatted stack line string + */ public static String formatFunctionStackLine(String functionName, String fileName, int lineNumber, String codeLine) { Preconditions.checkArgument(fileName != null); @@ -43,10 +73,22 @@ public static String formatFunctionStackLine(String functionName, String fileNam return "At function " + functionName + " (" + fileName + ":" + lineNumber + "):\n > " + codeLine.trim(); } + /** + * Returns a string representing the end of a stack trace. + * + * @return a string representing the end of a stack trace + */ public static String stackEnd() { return "\n"; } + /** + * Retrieves a specific line of code from a given code string. + * + * @param code the code string + * @param line the line number to retrieve + * @return the code line at the specified line number, or a message if the code is null + */ public static String getErrorLine(String code, int line) { if (code == null) { return "Could not get code."; diff --git a/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericActionListener.java b/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericActionListener.java index eeb1c066..f8dc6430 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericActionListener.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericActionListener.java @@ -1,11 +1,11 @@ -/** - * Copyright 2016 SPeCS. - * +/* + * Copyright 2016 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -18,23 +18,48 @@ import javax.swing.AbstractAction; +/** + * Generic implementation of ActionListener for Java Swing. + *

+ * Provides default (empty) implementation for the actionPerformed method. + *

+ */ public class GenericActionListener extends AbstractAction { /** - * + * Serial version UID for serialization. */ private static final long serialVersionUID = 1L; + /** + * Consumer to handle the action event. + */ private final Consumer consumer; + /** + * Creates a new instance of GenericActionListener with the given consumer. + * + * @param consumer the consumer to handle the action event + * @return a new instance of GenericActionListener + */ public static GenericActionListener newInstance(Consumer consumer) { return new GenericActionListener(consumer); } + /** + * Constructor for GenericActionListener. + * + * @param consumer the consumer to handle the action event + */ public GenericActionListener(Consumer consumer) { this.consumer = consumer; } + /** + * Invoked when an action occurs. + * + * @param e the action event + */ @Override public void actionPerformed(ActionEvent e) { consumer.accept(e); diff --git a/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericMouseListener.java b/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericMouseListener.java index 447495c7..248d9735 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericMouseListener.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericMouseListener.java @@ -1,14 +1,14 @@ -/** - * Copyright 2016 SPeCS. - * +/* + * Copyright 2016 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.util.swing; @@ -18,47 +18,105 @@ import java.util.function.Consumer; /** - * - * @author SpecS - * + * Generic implementation of MouseListener for Java Swing. + *

+ * Provides default (empty) implementations for all MouseListener methods. + *

*/ public class GenericMouseListener implements MouseListener { + /** + * Consumer to handle mouse click events. + */ private Consumer onClick; + + /** + * Consumer to handle mouse press events. + */ private Consumer onPress; + + /** + * Consumer to handle mouse release events. + */ private Consumer onRelease; + + /** + * Consumer to handle mouse entered events. + */ private Consumer onEntered; + + /** + * Consumer to handle mouse exited events. + */ private Consumer onExited; + /** + * Default constructor initializing all event handlers to empty implementations. + */ public GenericMouseListener() { onClick = onPress = onRelease = onEntered = onExited = empty(); } + /** + * Creates a GenericMouseListener with a specific click event handler. + * + * @param listener the Consumer to handle mouse click events + * @return a new GenericMouseListener instance + */ public static GenericMouseListener click(Consumer listener) { - return new GenericMouseListener().onClick(listener); } + /** + * Sets the click event handler. + * + * @param listener the Consumer to handle mouse click events + * @return the current GenericMouseListener instance + */ public GenericMouseListener onClick(Consumer listener) { onClick = listener; return this; } + /** + * Sets the press event handler. + * + * @param listener the Consumer to handle mouse press events + * @return the current GenericMouseListener instance + */ public GenericMouseListener onPressed(Consumer listener) { onPress = listener; return this; } + /** + * Sets the release event handler. + * + * @param listener the Consumer to handle mouse release events + * @return the current GenericMouseListener instance + */ public GenericMouseListener onRelease(Consumer listener) { onRelease = listener; return this; } + /** + * Sets the entered event handler. + * + * @param listener the Consumer to handle mouse entered events + * @return the current GenericMouseListener instance + */ public GenericMouseListener onEntered(Consumer listener) { onEntered = listener; return this; } + /** + * Sets the exited event handler. + * + * @param listener the Consumer to handle mouse exited events + * @return the current GenericMouseListener instance + */ public GenericMouseListener onExited(Consumer listener) { onExited = listener; return this; @@ -89,6 +147,11 @@ public void mouseExited(MouseEvent e) { onExited.accept(e); } + /** + * Provides an empty implementation for event handlers. + * + * @return a Consumer that does nothing + */ private static Consumer empty() { return e -> { }; diff --git a/SymjaPlus/src/pt/up/fe/specs/symja/SymjaPlusUtils.java b/SymjaPlus/src/pt/up/fe/specs/symja/SymjaPlusUtils.java index a5e9bb0d..85c1111e 100644 --- a/SymjaPlus/src/pt/up/fe/specs/symja/SymjaPlusUtils.java +++ b/SymjaPlus/src/pt/up/fe/specs/symja/SymjaPlusUtils.java @@ -1,14 +1,14 @@ /** * Copyright 2013 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.symja; @@ -26,43 +26,62 @@ import pt.up.fe.specs.util.SpecsCheck; /** + * Utility class for SymjaPlus operations, including expression simplification and conversion to C code. + * * @author Joao Bispo - * */ public class SymjaPlusUtils { + /** + * Thread-local evaluator for Symja expressions. + */ private static final ThreadLocal EVALUATOR = ThreadLocal .withInitial(() -> new ExprEvaluator(false, (short) 30)); + /** + * Simplifies a mathematical expression using Symja. + * + * @param expression the expression to simplify + * @return the simplified expression as a string + */ public static String simplify(String expression) { return simplify(expression, new HashMap<>()); } + /** + * Returns the thread-local Symja evaluator. + * + * @return the evaluator instance + */ private static ExprEvaluator evaluator() { return EVALUATOR.get(); } + /** + * Simplifies a mathematical expression using Symja, with support for constant definitions. + * + * @param expression the expression to simplify + * @param constants a map of constant names to values + * @return the simplified expression as a string + * @throws NullPointerException if constants is null + */ public static String simplify(String expression, Map constants) { - - // assert constants != null; SpecsCheck.checkNotNull(constants, () -> "Argument 'constants' cannot be null"); - - // Clear variables evaluator().clearVariables(); - for (String constantName : constants.keySet()) { String constantValue = constants.get(constantName); - - // String expr = constantName + " = " + constantValue; - evaluator().defineVariable(constantName, evaluator().eval(constantValue)); } - var output = evaluator().eval("expand(" + expression + ")").toString(); - return output; } + /** + * Converts a Symja expression to C code. + * + * @param expression the Symja expression + * @return the equivalent C code as a string + */ public static String convertToC(String expression) { // Convert to Symja AST var symjaNode = SymjaAst.parse(expression); diff --git a/SymjaPlus/src/pt/up/fe/specs/symja/ast/Operator.java b/SymjaPlus/src/pt/up/fe/specs/symja/ast/Operator.java index 24cd9a6f..8830d27d 100644 --- a/SymjaPlus/src/pt/up/fe/specs/symja/ast/Operator.java +++ b/SymjaPlus/src/pt/up/fe/specs/symja/ast/Operator.java @@ -1,55 +1,72 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.symja.ast; +/** + * Enum representing mathematical operators supported by Symja AST. + */ public enum Operator { + /** Addition operator. */ Plus("+", 2), + /** Subtraction operator. */ Minus("-", 2), + /** Multiplication operator. */ Times("*", 3), + /** Exponentiation operator. */ Power("^", 4), + /** Unary minus operator. */ UnaryMinus("-", 4); // What priority it should have? private final String symbol; private final int priority; + /** + * Constructs an Operator enum. + * + * @param symbol the operator symbol + * @param priority the operator precedence + */ private Operator(String symbol, int priority) { this.symbol = symbol; this.priority = priority; } + /** + * Gets the operator precedence. + * + * @return the priority value + */ public int getPriority() { return priority; } + /** + * Gets the operator symbol. + * + * @return the symbol as a string + */ public String getSymbol() { return symbol; } + /** + * Returns the Operator enum from a Symja symbol string. + * + * @param symjaSymbol the symbol string + * @return the corresponding Operator + */ public static Operator fromSymjaSymbol(String symjaSymbol) { - return Enum.valueOf(Operator.class, symjaSymbol); - - // switch (symjaSymbol) { - // case "Plus": - // return Plus; - // case "Times": - // return Times; - // case "Power": - // return Power; - // default: - // throw new CaseNotDefinedException(symjaSymbol); - // } } - } diff --git a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaAst.java b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaAst.java index 2bc17342..a11f9d1c 100644 --- a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaAst.java +++ b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaAst.java @@ -1,14 +1,14 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.symja.ast; @@ -24,8 +24,12 @@ import pt.up.fe.specs.util.SpecsCheck; import pt.up.fe.specs.util.classmap.FunctionClassMap; +/** + * Utility class for parsing and converting Symja AST nodes. + */ public class SymjaAst { + /** Function map for AST node type to SymjaNode converter. */ private static final FunctionClassMap CONVERTERS; static { CONVERTERS = new FunctionClassMap<>(); @@ -35,46 +39,51 @@ public class SymjaAst { CONVERTERS.put(ASTNode.class, SymjaAst::defaultConverter); } + /** + * Converts an IntegerNode to a SymjaInteger node. + * + * @param node the integer node + * @return the corresponding SymjaInteger node + */ private static SymjaInteger integerConverter(IntegerNode node) { var symbol = SymjaNode.newNode(SymjaInteger.class); - // System.out.println("IS SIGN: " + node.isSign()); - // System.out.println("INTEGER VALUE : " + node.getIntValue()); - // System.out.println("DOUBLE VALUE : " + node.doubleValue()); - // System.out.println("VALUE STRING: " + node.getString()); - // System.out.println("NUMBER FORMAT: " + node.getNumberFormat()); - // System.out.println("TO STRING: " + node.toString()); - // symbol.set(SymjaInteger.VALUE_STRING, node.getString()); symbol.set(SymjaInteger.VALUE_STRING, node.toString()); - return symbol; } + /** + * Converts a SymbolNode to a SymjaSymbol node. + * + * @param node the symbol node + * @return the corresponding SymjaSymbol node + */ private static SymjaNode symbolConverter(SymbolNode node) { var symbol = SymjaNode.newNode(SymjaSymbol.class); - symbol.set(SymjaSymbol.SYMBOL, node.getString()); - return symbol; } + /** + * Converts a SymbolNode to a SymjaOperator node. + * + * @param node the symbol node + * @return the corresponding SymjaOperator node + */ private static SymjaNode operatorConverter(SymbolNode node) { var symbol = SymjaNode.newNode(SymjaOperator.class); - var operator = Operator.fromSymjaSymbol(node.getString()); symbol.set(SymjaOperator.OPERATOR, operator); - return symbol; } + /** + * Converts a FunctionNode to a SymjaFunction node. + * + * @param node the function node + * @return the corresponding SymjaFunction node + */ private static SymjaNode functionConverter(FunctionNode node) { var children = new ArrayList(); - // var firstChild = node.get(0); - - // Up until now we only saw symbols - // SpecsCheck.checkClass(firstChild, SymbolNode.class); - - // children.add(operatorConverter((SymbolNode) firstChild)); - for (int i = 0; i < node.size(); i++) { if (i == 0) { SpecsCheck.checkClass(node.get(i), SymbolNode.class); @@ -83,45 +92,39 @@ private static SymjaNode functionConverter(FunctionNode node) { children.add(CONVERTERS.apply(node.get(i))); } } - // for (var child : node.subList(1, node.size())) { - // children.add(CONVERTERS.apply(child)); - // } - var function = SymjaNode.newNode(SymjaFunction.class, children); - return function; } + /** + * Default converter for ASTNode types not explicitly handled. + * + * @param node the AST node + * @return a generic SymjaNode + */ private static SymjaNode defaultConverter(ASTNode node) { System.out.println("NOT IMPLEMENTED: " + node.getClass()); return SymjaNode.newNode(SymjaNode.class); - // return new SymjaNode(null, null); } + /** + * Parses a Symja expression into a SymjaNode. + * + * @param symjaExpression the Symja expression + * @return the root SymjaNode + */ public static SymjaNode parse(String symjaExpression) { var p = new Parser(); - var root = p.parse(symjaExpression); - return toNode(root); - /* - for (var child : root) { - System.out.println("NODE: " + child.getClass()); - if (child instanceof SymbolNode) { - var symbol = (SymbolNode) child; - System.out.println("SYMBOL: " + symbol.getString()); - continue; - } - - if (child instanceof IntegerNode) { - var symbol = (IntegerNode) child; - System.out.println("INTEGER: " + symbol.toString()); - continue; - } - } - */ } + /** + * Converts an ASTNode to a SymjaNode. + * + * @param astNode the AST node + * @return the corresponding SymjaNode + */ public static SymjaNode toNode(ASTNode astNode) { return CONVERTERS.apply(astNode); } diff --git a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaFunction.java b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaFunction.java index da0fd543..3185f8fc 100644 --- a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaFunction.java +++ b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaFunction.java @@ -1,14 +1,14 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.symja.ast; @@ -19,11 +19,21 @@ import org.suikasoft.jOptions.Datakey.KeyFactory; import org.suikasoft.jOptions.Interfaces.DataStore; +/** + * Represents a function node in the Symja AST. + */ public class SymjaFunction extends SymjaNode { + /** DataKey indicating if the function has parenthesis. */ public static final DataKey HAS_PARENTHESIS = KeyFactory.bool("hasParenthesis") .setDefault(() -> true); + /** + * Constructs a SymjaFunction node. + * + * @param data the data store + * @param children the child nodes + */ public SymjaFunction(DataStore data, Collection children) { super(data, children); } diff --git a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaInteger.java b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaInteger.java index 52a89781..dbd9a654 100644 --- a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaInteger.java +++ b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaInteger.java @@ -1,14 +1,14 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.symja.ast; @@ -19,10 +19,20 @@ import org.suikasoft.jOptions.Datakey.KeyFactory; import org.suikasoft.jOptions.Interfaces.DataStore; +/** + * Represents an integer node in the Symja AST. + */ public class SymjaInteger extends SymjaNode { + /** DataKey for the string value of the integer. */ public static final DataKey VALUE_STRING = KeyFactory.string("valueString"); + /** + * Constructs a SymjaInteger node. + * + * @param data the data store + * @param children the child nodes + */ public SymjaInteger(DataStore data, Collection children) { super(data, children); } diff --git a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaNode.java b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaNode.java index 8a8c2227..a941eb70 100644 --- a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaNode.java +++ b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaNode.java @@ -1,14 +1,14 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.symja.ast; @@ -22,27 +22,45 @@ import pt.up.fe.specs.util.SpecsSystem; +/** + * Base class for nodes in the Symja AST. + */ public class SymjaNode extends DataNode { + /** + * Creates a new node of the given class with no children. + * + * @param nodeClass the class of the node + * @param the node type + * @return a new node instance + */ public static T newNode(Class nodeClass) { return newNode(nodeClass, Collections.emptyList()); } + /** + * Creates a new node of the given class with the specified children. + * + * @param nodeClass the class of the node + * @param children the child nodes + * @param the node type + * @return a new node instance + */ public static T newNode(Class nodeClass, Collection children) { DataStore data = DataStore.newInstance(StoreDefinitions.fromInterface(nodeClass), true); return SpecsSystem.newInstance(nodeClass, data, children); - // return (T) new SymjaNode(data, children); } + /** + * Constructs a SymjaNode with the given data and children. + * + * @param data the data store + * @param children the child nodes + */ public SymjaNode(DataStore data, Collection children) { super(data, children); } - // @Override - // public String toContentString() { - // return getData().toInlinedString(); - // } - @Override protected SymjaNode getThis() { return this; diff --git a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaOperator.java b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaOperator.java index 1d4e5106..948af347 100644 --- a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaOperator.java +++ b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaOperator.java @@ -1,14 +1,14 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.symja.ast; @@ -19,21 +19,21 @@ import org.suikasoft.jOptions.Datakey.KeyFactory; import org.suikasoft.jOptions.Interfaces.DataStore; +/** + * Represents an operator node in the Symja AST. + */ public class SymjaOperator extends SymjaNode { + /** DataKey for the operator. */ public static final DataKey OPERATOR = KeyFactory.object("operator", Operator.class); - // public static final DataKey SYMBOL = KeyFactory.string("symbol"); - // public static final DataKey PRIORITY = KeyFactory.integer("priority"); + /** + * Constructs a SymjaOperator node. + * + * @param data the data store + * @param children the child nodes + */ public SymjaOperator(DataStore data, Collection children) { super(data, children); } - - // public int getPriority() { - // switch (get(SYMBOL)) { - // case - // default: - // throw new CaseNotDefinedException(get(SYMBOL)); - // } - // } } diff --git a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaSymbol.java b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaSymbol.java index 5ff5fa14..956fc5c1 100644 --- a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaSymbol.java +++ b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaSymbol.java @@ -1,14 +1,14 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.symja.ast; @@ -19,10 +19,20 @@ import org.suikasoft.jOptions.Datakey.KeyFactory; import org.suikasoft.jOptions.Interfaces.DataStore; +/** + * Represents a symbol node in the Symja AST. + */ public class SymjaSymbol extends SymjaNode { + /** DataKey for the symbol string. */ public static final DataKey SYMBOL = KeyFactory.string("symbol"); + /** + * Constructs a SymjaSymbol node. + * + * @param data the data store + * @param children the child nodes + */ public SymjaSymbol(DataStore data, Collection children) { super(data, children); } diff --git a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaToC.java b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaToC.java index a0117490..e6caeebe 100644 --- a/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaToC.java +++ b/SymjaPlus/src/pt/up/fe/specs/symja/ast/SymjaToC.java @@ -1,14 +1,14 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.symja.ast; @@ -19,8 +19,12 @@ import pt.up.fe.specs.util.classmap.FunctionClassMap; import pt.up.fe.specs.util.exceptions.CaseNotDefinedException; +/** + * Utility class for converting Symja AST nodes to C code. + */ public class SymjaToC { + /** Function map for node type to converter. */ private static final FunctionClassMap CONVERTERS; static { CONVERTERS = new FunctionClassMap<>(); @@ -30,14 +34,32 @@ public class SymjaToC { CONVERTERS.put(SymjaNode.class, SymjaToC::defaultConverter); } + /** + * Converts a SymjaSymbol node to C code. + * + * @param node the symbol node + * @return the symbol as a string + */ private static String symbolConverter(SymjaSymbol node) { return node.get(SymjaSymbol.SYMBOL); } + /** + * Converts a SymjaInteger node to C code. + * + * @param node the integer node + * @return the integer value as a string + */ private static String integerConverter(SymjaInteger node) { return node.get(SymjaInteger.VALUE_STRING); } + /** + * Converts a SymjaFunction node to C code. + * + * @param node the function node + * @return the C code as a string + */ private static String functionConverter(SymjaFunction node) { var firstChild = node.getChild(0); @@ -58,6 +80,13 @@ private static String functionConverter(SymjaFunction node) { return code; } + /** + * Converts an operator and its operands to C code. + * + * @param operator the operator + * @param operands the operand nodes + * @return the C code as a string + */ private static String convertOperator(SymjaOperator operator, List operands) { var symbol = operator.get(SymjaOperator.OPERATOR); switch (symbol) { @@ -76,6 +105,13 @@ private static String convertOperator(SymjaOperator operator, List op } + /** + * Converts a binary operator and its operands to C code. + * + * @param operator the operator + * @param operands the operand nodes + * @return the C code as a string + */ private static String convertTwoOperandsOperator(Operator operator, List operands) { StringBuilder code = new StringBuilder(); @@ -88,6 +124,14 @@ private static String convertTwoOperandsOperator(Operator operator, List"; } + /** + * Converts a SymjaNode to C code. + * + * @param node the Symja node + * @return the C code as a string + */ public static String convert(SymjaNode node) { return CONVERTERS.apply(node); } diff --git a/SymjaPlus/src/pt/up/fe/specs/symja/ast/VisitAllTransform.java b/SymjaPlus/src/pt/up/fe/specs/symja/ast/VisitAllTransform.java index e83046ad..cb678bf3 100644 --- a/SymjaPlus/src/pt/up/fe/specs/symja/ast/VisitAllTransform.java +++ b/SymjaPlus/src/pt/up/fe/specs/symja/ast/VisitAllTransform.java @@ -1,14 +1,14 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.symja.ast; @@ -17,15 +17,29 @@ import pt.up.fe.specs.util.treenode.transform.TransformResult; import pt.up.fe.specs.util.treenode.transform.TransformRule; +/** + * Interface for transforms that visit all nodes in a Symja AST. + */ public interface VisitAllTransform extends TransformRule { + /** + * Applies the transform to the given node and its children. + * + * @param node the node to transform + * @param queue the transform queue + * @return the result of the transformation + */ @Override default TransformResult apply(SymjaNode node, TransformQueue queue) { applyAll(node, queue); - return TransformResult.empty(); - } + /** + * Applies the transform to all children of the given node. + * + * @param node the node to transform + * @param queue the transform queue + */ void applyAll(SymjaNode node, TransformQueue queue); } diff --git a/SymjaPlus/src/pt/up/fe/specs/symja/ast/passes/RemoveMinusMultTransform.java b/SymjaPlus/src/pt/up/fe/specs/symja/ast/passes/RemoveMinusMultTransform.java index 6fb58834..56938866 100644 --- a/SymjaPlus/src/pt/up/fe/specs/symja/ast/passes/RemoveMinusMultTransform.java +++ b/SymjaPlus/src/pt/up/fe/specs/symja/ast/passes/RemoveMinusMultTransform.java @@ -1,14 +1,14 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.symja.ast.passes; @@ -26,58 +26,57 @@ import pt.up.fe.specs.util.treenode.transform.TransformQueue; import pt.up.fe.specs.util.treenode.transform.util.TraversalStrategy; +/** + * Transform that replaces multiplication by -1 with a unary minus in the Symja AST. + */ public class RemoveMinusMultTransform implements VisitAllTransform { + /** + * Applies the transform to all children of the given node, replacing multiplication by -1 where appropriate. + * + * @param node the node to transform + * @param queue the transform queue + */ @Override public void applyAll(SymjaNode node, TransformQueue queue) { - if (!(node instanceof SymjaFunction)) { return; } - var operator = node.getChild(SymjaOperator.class, 0); - var symbol = operator.get(SymjaOperator.OPERATOR); if (symbol != Operator.Times) { return; } - var leftOperand = node.getChild(1); if (!SymjaToC.convert(leftOperand).equals("-1")) { return; } - // if (!leftOperand.toString().equals("-1")) { - - // } - var rightOperand = node.getChild(2); - if (rightOperand instanceof SymjaInteger) { var newInteger = SymjaNode.newNode(SymjaInteger.class); newInteger.set(SymjaInteger.VALUE_STRING, rightOperand.get(SymjaInteger.VALUE_STRING)); var unaryMinus = SymjaNode.newNode(SymjaOperator.class); unaryMinus.set(SymjaOperator.OPERATOR, Operator.UnaryMinus); - var newFunction = SymjaNode.newNode(SymjaFunction.class, Arrays.asList(unaryMinus, newInteger)); - queue.replace(node, newFunction); return; } - if (rightOperand instanceof SymjaSymbol) { var newSymbol = SymjaNode.newNode(SymjaSymbol.class); newSymbol.set(SymjaSymbol.SYMBOL, rightOperand.get(SymjaSymbol.SYMBOL)); var unaryMinus = SymjaNode.newNode(SymjaOperator.class); unaryMinus.set(SymjaOperator.OPERATOR, Operator.UnaryMinus); - var newFunction = SymjaNode.newNode(SymjaFunction.class, Arrays.asList(unaryMinus, newSymbol)); - queue.replace(node, newFunction); return; } - } + /** + * Returns the traversal strategy for this transform. + * + * @return the traversal strategy + */ @Override public TraversalStrategy getTraversalStrategy() { return TraversalStrategy.POST_ORDER; diff --git a/SymjaPlus/src/pt/up/fe/specs/symja/ast/passes/RemoveRedundantParenthesisTransform.java b/SymjaPlus/src/pt/up/fe/specs/symja/ast/passes/RemoveRedundantParenthesisTransform.java index efdfcf9a..629da454 100644 --- a/SymjaPlus/src/pt/up/fe/specs/symja/ast/passes/RemoveRedundantParenthesisTransform.java +++ b/SymjaPlus/src/pt/up/fe/specs/symja/ast/passes/RemoveRedundantParenthesisTransform.java @@ -1,14 +1,14 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.symja.ast.passes; @@ -20,46 +20,47 @@ import pt.up.fe.specs.util.treenode.transform.TransformQueue; import pt.up.fe.specs.util.treenode.transform.util.TraversalStrategy; +/** + * Transform that removes redundant parenthesis from Symja AST function nodes. + */ public class RemoveRedundantParenthesisTransform implements VisitAllTransform { + /** + * Applies the transform to all children of the given node, removing redundant parenthesis where appropriate. + * + * @param node the node to transform + * @param queue the transform queue + */ @Override public void applyAll(SymjaNode node, TransformQueue queue) { - if (!(node instanceof SymjaFunction)) { return; } - if (!node.hasParent()) { node.set(SymjaFunction.HAS_PARENTHESIS, false); return; } - var parent = node.getParent(); - if (!(parent instanceof SymjaFunction)) { return; } - var operator = (SymjaOperator) node.getChild(0); var parentOperator = (SymjaOperator) parent.getChild(0); - var operatorPriority = operator.get(SymjaOperator.OPERATOR).getPriority(); var parentPriority = parentOperator.get(SymjaOperator.OPERATOR).getPriority(); - if (operatorPriority > parentPriority) { node.set(SymjaFunction.HAS_PARENTHESIS, false); } else if (operatorPriority == parentPriority && parent.getChild(1) == node) { node.set(SymjaFunction.HAS_PARENTHESIS, false); } - - // System.out.println("OPERATOR: " + node.getChild(0)); - // System.out.println("PARENT OPERATOR: " + parent.getChild(0)); - // var operator = node.getChild(SymjaOperator.class, 0); - return; - } + /** + * Returns the traversal strategy for this transform. + * + * @return the traversal strategy + */ @Override public TraversalStrategy getTraversalStrategy() { return TraversalStrategy.POST_ORDER; diff --git a/SymjaPlus/src/pt/up/fe/specs/symja/ast/passes/ReplaceUnaryMinusTransform.java b/SymjaPlus/src/pt/up/fe/specs/symja/ast/passes/ReplaceUnaryMinusTransform.java index 187c18e0..03c7a271 100644 --- a/SymjaPlus/src/pt/up/fe/specs/symja/ast/passes/ReplaceUnaryMinusTransform.java +++ b/SymjaPlus/src/pt/up/fe/specs/symja/ast/passes/ReplaceUnaryMinusTransform.java @@ -1,14 +1,14 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package pt.up.fe.specs.symja.ast.passes; @@ -23,11 +23,19 @@ import pt.up.fe.specs.util.treenode.transform.TransformQueue; import pt.up.fe.specs.util.treenode.transform.util.TraversalStrategy; +/** + * Transform that replaces unary minus operations in the Symja AST with equivalent binary operations. + */ public class ReplaceUnaryMinusTransform implements VisitAllTransform { + /** + * Applies the transform to all children of the given node, replacing unary minus where appropriate. + * + * @param node the node to transform + * @param queue the transform queue + */ @Override public void applyAll(SymjaNode node, TransformQueue queue) { - if (!(node instanceof SymjaFunction)) { return; } @@ -64,23 +72,16 @@ public void applyAll(SymjaNode node, TransformQueue queue) { var newOperator = SymjaNode.newNode(SymjaOperator.class); newOperator.set(SymjaOperator.OPERATOR, newOperatorSymbol); - - // System.out.println("FIRST OPERAND: " + parent.getChild(1)); - // System.out.println("Second OPERAND: " + node.getChild(1)); - var newFunction = SymjaNode.newNode(SymjaFunction.class, Arrays.asList(newOperator, parent.getChild(1).copy(), node.getChild(1))); - - // System.out.println("1: " + parent.getChild(1)); - // System.out.println("CHILDREN: " + newFunction.getChildren()); - // System.out.println("NEW F: " + SymjaToC.convert(newFunction)); queue.replace(parent, newFunction); - - // System.out.println("OPERATOR: " + node.getChild(0)); - // System.out.println("PARENT OPERATOR: " + parent.getChild(0)); - } + /** + * Returns the traversal strategy for this transform. + * + * @return the traversal strategy + */ @Override public TraversalStrategy getTraversalStrategy() { return TraversalStrategy.POST_ORDER; diff --git a/XStreamPlus/src/org/suikasoft/XStreamPlus/MappingsCollector.java b/XStreamPlus/src/org/suikasoft/XStreamPlus/MappingsCollector.java index 28736d00..d7f441bd 100644 --- a/XStreamPlus/src/org/suikasoft/XStreamPlus/MappingsCollector.java +++ b/XStreamPlus/src/org/suikasoft/XStreamPlus/MappingsCollector.java @@ -1,14 +1,14 @@ /* * Copyright 2011 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.XStreamPlus; @@ -24,131 +24,94 @@ /** * Collects the alias mappings for an XstreamObject. - * + * * @author Joao Bispo */ public class MappingsCollector { private Set> collectedClasses; + /** + * Creates a new MappingsCollector instance. + */ public MappingsCollector() { - collectedClasses = null; + collectedClasses = null; } + /** + * Collects all alias mappings for the given ObjectXml instance. + * + * @param object the ObjectXml instance + * @return a map of alias to class + */ public Map> collectMappings(ObjectXml object) { - // public Map> collectMappings(ObjectXml object) { - collectedClasses = new HashSet<>(); - - return collectMappingsInternal(object); + collectedClasses = new HashSet<>(); + return collectMappingsInternal(object); } + /** + * Recursively collects alias mappings from the given ObjectXml and its nested ObjectXmls. + * + * @param object the ObjectXml to process + * @return a map of alias to class for the given object and its nested objects + */ private Map> collectMappingsInternal(ObjectXml object) { - // private Map> collectMappingsInternal(ObjectXml object) { - Map> mappings = new HashMap<>(); - - // Check if object was already collected - if (collectedClasses.contains(object.getClass())) { - return mappings; - } - collectedClasses.add(object.getClass()); - - // System.out.println("Fields of '"+object.getTargetClass()+"':"); - // TODO: Move this functionality to another method - for (Field field : object.getTargetClass().getDeclaredFields()) { - Class fieldType = field.getType(); - boolean implementsXmlSerializable = SpecsSystem - .implementsInterface(fieldType, XmlSerializable.class); - if (!implementsXmlSerializable) { - continue; - } - - // Check if class was taken in to account - ObjectXml nestedXml = object.getNestedXml().get(fieldType); - // ObjectXml nestedXml = object.getNestedXml().get(fieldType); - if (nestedXml == null) { - SpecsLogs.warn("Class '" + fieldType.getSimpleName() - + "' which implements interface '" - + XmlSerializable.class.getSimpleName() - + "' was not added to the nested XMLs of '" - + object.getTargetClass().getSimpleName() + "'."); - SpecsLogs - .msgWarn("Please use protected method 'addNestedXml' in '" - + object.getClass().getSimpleName() - + "' to add the XmlObject before initiallizing the XStreamFile object."); - continue; - } - - Map> childrenMappings = collectMappingsInternal(nestedXml); - addMappings(mappings, childrenMappings); - } - // System.out.println(Arrays.asList(object.getTargetClass().getDeclaredFields())); - // INFO: Checking does not work because XmlObject files do not add the - // object which implement the XmlSerializable interface, usually they do - // not have alias problems. - - // Collect added classes to check later if there was any class that - // implements - // XmlSerializable that was left out. - // Set nestedTargetClasses = new HashSet(); - - // Check XstreamObjects inside first, to follow the hierarchy - /* - * if(NestedXml.class.isInstance(object)) { // Collect all mappings - * for(ObjectXml xObj : ((NestedXml) object).getObjects()) { Map childrenMappings = collectMappingsInternal(xObj); - * addMappings(mappings, childrenMappings); - * //nestedTargetClasses.add(xObj.getTargetClass()); } } - */ - - // Add own mappings - Map> ownMappings = object.getMappings(); - if (ownMappings == null) { - ownMappings = new HashMap<>(); - } - - // Check if any of the classes in ownMapping implements XmlSerializable, - // and in that case, check if we are already taking that into account. - // checkPossbileNestedClasses(ownMappings, nestedTargetClasses); - - addMappings(mappings, ownMappings); - return mappings; + Map> mappings = new HashMap<>(); + if (collectedClasses.contains(object.getClass())) { + return mappings; + } + collectedClasses.add(object.getClass()); + for (Field field : object.getTargetClass().getDeclaredFields()) { + Class fieldType = field.getType(); + boolean implementsXmlSerializable = SpecsSystem + .implementsInterface(fieldType, XmlSerializable.class); + if (!implementsXmlSerializable) { + continue; + } + ObjectXml nestedXml = object.getNestedXml().get(fieldType); + if (nestedXml == null) { + SpecsLogs.warn("Class '" + fieldType.getSimpleName() + + "' which implements interface '" + + XmlSerializable.class.getSimpleName() + + "' was not added to the nested XMLs of '" + + object.getTargetClass().getSimpleName() + "'."); + // TODO: Move this functionality to another method + SpecsLogs.warn("Please use protected method 'addNestedXml' in '" + + object.getClass().getSimpleName() + + "' to add the XmlObject before initiallizing the XStreamFile object."); + continue; + } + Map> childrenMappings = collectMappingsInternal(nestedXml); + addMappings(mappings, childrenMappings); + } + Map> ownMappings = object.getMappings(); + if (ownMappings == null) { + ownMappings = new HashMap<>(); + } + addMappings(mappings, ownMappings); + return mappings; } + /** + * Adds mappings from newMappings into totalMappings, warning if an alias is already present. + * + * @param totalMappings the map to add to + * @param newMappings the map of new mappings to add + */ private static void addMappings(Map> totalMappings, - Map> newMappings) { - // Add children mappings - for (String key : newMappings.keySet()) { - Class childClass = newMappings.get(key); - if (totalMappings.containsKey(key)) { - Class definedClass = totalMappings.get(key); - SpecsLogs.getLogger().warning( - "Alias '" + key + "' is already defined for class '" - + definedClass - + "'. Skipping this mapping for class '" - + childClass + "'."); - continue; - } - - totalMappings.put(key, childClass); - } + Map> newMappings) { + for (String key : newMappings.keySet()) { + Class childClass = newMappings.get(key); + if (totalMappings.containsKey(key)) { + Class definedClass = totalMappings.get(key); + SpecsLogs.getLogger().warning( + "Alias '" + key + "' is already defined for class '" + + definedClass + + "'. Skipping this mapping for class '" + + childClass + "'."); + continue; + } + totalMappings.put(key, childClass); + } } - /* - * private void checkPossbileNestedClasses(Map ownMappings, - * Set nestedTargetClasses) { for(String key : ownMappings.keySet()) - * { // Check for each class, if they implement the interface - * XmlSerializable Class aClass = ownMappings.get(key); //Set - * fieldInterfaces = new HashSet(); //boolean - * implementsXmlSerializable = implementsInterface(aClass, - * XmlSerializable.class); boolean implementsXmlSerializable = - * ProcessUtils.implementsInterface(aClass, XmlSerializable.class); - * if(!implementsXmlSerializable) { return; } - * - * // Check if class was already taken into account - * if(nestedTargetClasses.contains(aClass)) { return; } - * - * LoggingUtils.getLogger(). - * warning("One of the mapped classes implements interface '"+ - * XmlSerializable.class+"' and was not taken into account."); } } - */ - } diff --git a/XStreamPlus/src/org/suikasoft/XStreamPlus/ObjectXml.java b/XStreamPlus/src/org/suikasoft/XStreamPlus/ObjectXml.java index 12277f1e..73a63508 100644 --- a/XStreamPlus/src/org/suikasoft/XStreamPlus/ObjectXml.java +++ b/XStreamPlus/src/org/suikasoft/XStreamPlus/ObjectXml.java @@ -1,11 +1,11 @@ /* * Copyright 2011 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -23,12 +23,12 @@ /** * Base for transforming an object to and from XML. - * + * *

* When implementing this class do not let the XStreamFile object escape to outside of the class, or you might not be * able to guarantee the correct behavior of custom toXml() and fromXml() implementations. - * - * @author Joao Bispo + * + * @param the type handled by this ObjectXml */ public abstract class ObjectXml { @@ -36,34 +36,52 @@ public abstract class ObjectXml { private final Map> mappings = new HashMap<>(); private final XStreamFile xstreamFile; + /** + * Constructs a new ObjectXml and initializes its XStreamFile. + */ public ObjectXml() { xstreamFile = new XStreamFile<>(this); } /** * Alias mappings, for assigning names to classes. Can be null. - * - * @return + * + * @return the alias-to-class mappings */ public Map> getMappings() { return mappings; } + /** + * Adds a mapping from alias to class. + * + * @param name the alias + * @param aClass the class + */ public void addMappings(String name, Class aClass) { if (mappings.containsKey(name)) { throw new RuntimeException("Mapping for name '" + name + "' already present"); } - mappings.put(name, aClass); xstreamFile.getXstream().alias(name, aClass); } + /** + * Adds multiple mappings from a map. + * + * @param mappings the map of alias-to-class + */ public void addMappings(Map> mappings) { for (Entry> entry : mappings.entrySet()) { addMappings(entry.getKey(), entry.getValue()); } } + /** + * Adds mappings for a list of classes, using their simple names as aliases. + * + * @param classes the list of classes + */ public void addMappings(List> classes) { for (Class aClass : classes) { addMappings(aClass.getSimpleName(), aClass); @@ -72,24 +90,45 @@ public void addMappings(List> classes) { /** * The class that will be transformed to and from XML. - * - * @return + * + * @return the target class */ public abstract Class getTargetClass(); + /** + * Serializes the given object to XML. + * + * @param object the object to serialize + * @return the XML string + */ public String toXml(Object object) { return getXStreamFile().toXml(object); } + /** + * Deserializes the given XML string to an object of type T. + * + * @param xmlContents the XML string + * @return the deserialized object + */ public T fromXml(String xmlContents) { return getXStreamFile().fromXml(xmlContents); } + /** + * Returns the XStreamFile used by this ObjectXml. + * + * @return the XStreamFile + */ protected XStreamFile getXStreamFile() { return xstreamFile; - // return new XStreamFile<>(this); } + /** + * Adds a nested ObjectXml for a specific class. + * + * @param objectXml the nested ObjectXml + */ protected void addNestedXml(ObjectXml objectXml) { ObjectXml returnObject = nestedXml.put(objectXml.getTargetClass(), objectXml); if (returnObject != null) { @@ -98,12 +137,22 @@ protected void addNestedXml(ObjectXml objectXml) { } } + /** + * Returns the map of nested ObjectXml instances. + * + * @return the nested ObjectXml map + */ public Map, ObjectXml> getNestedXml() { return nestedXml; } + /** + * Registers a custom converter for a specific class. + * + * @param supportedClass the class supported by the converter + * @param converter the converter implementation + */ public void registerConverter(Class supportedClass, StringCodec converter) { getXStreamFile().getXstream().registerConverter(new StringConverter<>(supportedClass, converter)); } - } diff --git a/XStreamPlus/src/org/suikasoft/XStreamPlus/StringConverter.java b/XStreamPlus/src/org/suikasoft/XStreamPlus/StringConverter.java index 80c0b05f..293bbac1 100644 --- a/XStreamPlus/src/org/suikasoft/XStreamPlus/StringConverter.java +++ b/XStreamPlus/src/org/suikasoft/XStreamPlus/StringConverter.java @@ -1,43 +1,71 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.XStreamPlus; import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter; - import pt.up.fe.specs.util.parsing.StringCodec; +/** + * Converter for serializing and deserializing objects using a StringCodec with XStream. + * + * @param the type supported by this converter + */ public class StringConverter extends AbstractSingleValueConverter { private final Class supportedClass; private final StringCodec codec; + /** + * Constructs a StringConverter for the given class and codec. + * + * @param supportedClass the class supported by this converter + * @param codec the codec to use for encoding/decoding + */ public StringConverter(Class supportedClass, StringCodec codec) { this.supportedClass = supportedClass; this.codec = codec; } + /** + * Checks if the converter can handle the given type. + * + * @param type the class to check + * @return true if the type is supported, false otherwise + */ @SuppressWarnings("rawtypes") @Override public boolean canConvert(Class type) { return supportedClass.isAssignableFrom(type); } + /** + * Converts a string to an object using the codec. + * + * @param str the string to decode + * @return the decoded object + */ @Override public Object fromString(String str) { return codec.decode(str); } + /** + * Converts an object to a string using the codec. + * + * @param obj the object to encode + * @return the encoded string + */ @SuppressWarnings("unchecked") @Override public String toString(Object obj) { diff --git a/XStreamPlus/src/org/suikasoft/XStreamPlus/XStreamFile.java b/XStreamPlus/src/org/suikasoft/XStreamPlus/XStreamFile.java index 9f6ea712..ce7d5f17 100644 --- a/XStreamPlus/src/org/suikasoft/XStreamPlus/XStreamFile.java +++ b/XStreamPlus/src/org/suikasoft/XStreamPlus/XStreamFile.java @@ -1,11 +1,11 @@ /* * Copyright 2011 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -24,14 +24,14 @@ import pt.up.fe.specs.util.SpecsLogs; /** - * + * Utility for serializing and deserializing objects to and from XML using XStream. + * + * @param the type handled by this XStreamFile * @author Joao Bispo */ public class XStreamFile { - /** - * INSTANCE VARIABLES - */ + /** INSTANCE VARIABLES */ private final ObjectXml config; public final static Set reservedAlias; public final XStream xstream; @@ -39,35 +39,56 @@ public class XStreamFile { static { reservedAlias = new HashSet<>(); - reservedAlias.add("string"); reservedAlias.add("int"); - } + /** + * Constructs a new XStreamFile for the given ObjectXml configuration. + * + * @param object the ObjectXml configuration + */ public XStreamFile(ObjectXml object) { this.config = object; xstream = newXStream(); useCompactRepresentation = false; - // xstream.addPermission(new AnyTypePermission()); - // xstream.allowTypesByWildcard(new String[] { - // "org.suikasoft.**", - // "com.mydomain.utilitylibraries.**" - // }); } + /** + * Creates a new XStreamFile instance for the given ObjectXml configuration. + * + * @param object the ObjectXml configuration + * @param the type handled + * @return a new XStreamFile instance + */ public static XStreamFile newInstance(ObjectXml object) { return new XStreamFile<>(object); } + /** + * Sets whether to use compact XML representation. + * + * @param useCompactRepresentation true for compact, false for pretty + */ public void setUseCompactRepresentation(boolean useCompactRepresentation) { this.useCompactRepresentation = useCompactRepresentation; } + /** + * Returns the underlying XStream instance. + * + * @return the XStream instance + */ public XStream getXstream() { return xstream; } + /** + * Serializes the given object to XML. + * + * @param object the object to serialize + * @return the XML string, or null if the object is not compatible + */ public String toXml(Object object) { if (!(config.getTargetClass().isInstance(object))) { SpecsLogs.getLogger().warning( @@ -75,16 +96,20 @@ public String toXml(Object object) { + "compatible with class '" + config.getTargetClass() + "'."); return null; } - if (useCompactRepresentation) { StringWriter sw = new StringWriter(); xstream.marshal(object, new CompactWriter(sw)); return sw.toString(); } - return getXstream().toXML(object); } + /** + * Deserializes the given XML string to an object of type T. + * + * @param xmlContents the XML string + * @return the deserialized object, or null if not compatible + */ public T fromXml(String xmlContents) { Object dataInstance = xstream.fromXML(xmlContents); if (!config.getTargetClass().isInstance(dataInstance)) { @@ -92,15 +117,17 @@ public T fromXml(String xmlContents) { "Given file does not represent a '" + config.getTargetClass() + "' object."); return null; } - - // if(config.getTargetClass().isAssignableFrom(dataInstance.getClass())) { if (!config.getTargetClass().isInstance(dataInstance)) { return null; } - return config.getTargetClass().cast(dataInstance); } + /** + * Creates a new XStream instance with default configuration. + * + * @return a new XStream instance + */ private XStream newXStream() { MappingsCollector mappingsCollector = new MappingsCollector(); Map> mappings = mappingsCollector.collectMappings(config); diff --git a/XStreamPlus/src/org/suikasoft/XStreamPlus/XStreamUtils.java b/XStreamPlus/src/org/suikasoft/XStreamPlus/XStreamUtils.java index a530cc9c..607499c3 100644 --- a/XStreamPlus/src/org/suikasoft/XStreamPlus/XStreamUtils.java +++ b/XStreamPlus/src/org/suikasoft/XStreamPlus/XStreamUtils.java @@ -1,11 +1,11 @@ /* * Copyright 2011 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -24,78 +24,32 @@ import pt.up.fe.specs.util.SpecsLogs; /** - * Utility methods related to XStreamPlus package. - * - *

- * Ex.: reading and writing ObjectXml objects to and from XML files. - * - * @author Joao Bispo + * Utility methods related to XStreamPlus package, such as reading and writing ObjectXml objects to and from XML files. */ public class XStreamUtils { + /** + * Creates a new XStream instance with default permissions and converters. + * + * @return a configured XStream instance + */ public static XStream newXStream() { - var xstream = new XStream(); xstream.addPermission(new AnyTypePermission()); - // xstream.registerConverter(new OptionalConverter(xstream)); xstream.registerConverter(new OptionalConverter()); - // var xstream = new XStream(new DomDriver()); - - // XStream.setupDefaultSecurity(xstream); - // xstream.allowTypesByWildcard(new String[] { - // "java.**" - // }); - return xstream; - - // return new XStream(); - // return new XStream(new DomDriver()); - - // Taken from here: https://github.com/x-stream/xstream/issues/101#issuecomment-514760040 - // XStream xstream = new XStream(new StaxDriver() { - // @Override - // public HierarchicalStreamWriter createWriter(Writer out) { - // return new PrettyPrintWriter(out, " "); - // } - // }) { - // // only register the converters we need; other converters generate a private access warning in the console - // // on Java9+... - // @Override - // protected void setupConverters() { - // registerConverter(new NullConverter(), PRIORITY_VERY_HIGH); - // registerConverter(new IntConverter(), PRIORITY_NORMAL); - // registerConverter(new FloatConverter(), PRIORITY_NORMAL); - // registerConverter(new DoubleConverter(), PRIORITY_NORMAL); - // registerConverter(new LongConverter(), PRIORITY_NORMAL); - // registerConverter(new ShortConverter(), PRIORITY_NORMAL); - // registerConverter(new BooleanConverter(), PRIORITY_NORMAL); - // registerConverter(new ByteConverter(), PRIORITY_NORMAL); - // registerConverter(new StringConverter(), PRIORITY_NORMAL); - // registerConverter(new DateConverter(), PRIORITY_NORMAL); - // registerConverter(new CollectionConverter(getMapper()), PRIORITY_NORMAL); - // registerConverter(new ReflectionConverter(getMapper(), getReflectionProvider()), PRIORITY_VERY_LOW); - // } - // }; - // xstream.autodetectAnnotations(true); - // - // // setup proper security by limiting which classes can be loaded by XStream - // // xstream.addPermission(NoTypePermission.NONE); - // // xstream.addPermission(new WildcardTypePermission(new String[] { "com.mycompany.**" })); - // - // return xstream; - } - /* - * public static boolean write(File file, XmlSerializable object) { return - * write(file, object, object.getXmlSerializer()); } + /** + * Writes an object to a file using the provided ObjectXml stream. + * + * @param file the file to write to + * @param object the object to write + * @param stream the ObjectXml stream to use for serialization + * @param the type of the object + * @return true if the write operation was successful, false otherwise */ - - public static boolean write(File file, Object object, - ObjectXml stream) { - // public static boolean write(File file, T object, ObjectXml - // stream) { - + public static boolean write(File file, Object object, ObjectXml stream) { String xmlContents = stream.toXml(object); if (xmlContents == null) { SpecsLogs.getLogger().warning("Could not generate XML."); @@ -106,24 +60,19 @@ public static boolean write(File file, Object object, } /** - * Generic implementation of write method, without user-defined mappings. - * - * @param file - * @param object - * @return + * Writes an object to a file using a generic implementation without user-defined mappings. + * + * @param file the file to write to + * @param object the object to write + * @param objectClass the class of the object + * @param the type of the object + * @return true if the write operation was successful, false otherwise */ - // public static boolean write(File file, final Object object) { - public static boolean write(File file, final T object, - final Class objectClass) { - // ObjectXml objXml = new ObjectXml() { + public static boolean write(File file, final T object, final Class objectClass) { ObjectXml objXml = new ObjectXml() { - - // @SuppressWarnings("unchecked") - // @Override @Override public Class getTargetClass() { return objectClass; - // return (Class)object.getClass(); } }; @@ -131,40 +80,28 @@ public Class getTargetClass() { } /** - * The XML representation of the object. - * - * TODO: Change name to toXml, after errors are corrected - * - * @param file - * @param object - * @return + * Converts an object to its XML representation. + * + * @param object the object to convert + * @return the XML representation of the object */ public static String toString(final Object object) { - // public static String toString(final T object) { - // ObjectXml objXml = new ObjectXml() { - /* - * ObjectXml objXml = new ObjectXml() { - * - * @Override //public Class getTargetClass() { public Class - * getTargetClass() { return (Class) object.getClass(); // - * Class aClass = object.getClass(); // return aClass; } }; - * - * - * return objXml.toXml(object); - */ XStream xstream = XStreamUtils.newXStream(); return xstream.toXML(object); } + /** + * Reads an object from a file using the provided ObjectXml stream. + * + * @param file the file to read from + * @param stream the ObjectXml stream to use for deserialization + * @param the type of the object + * @return the deserialized object, or null if the operation failed + */ public static T read(File file, ObjectXml stream) { - // public static T read(File file, ObjectXml stream) { - // public static Object read(File file, ObjectXml stream) { String xmlContents = SpecsIo.read(file); T newObject = stream.fromXml(xmlContents); - // T newObject = stream.fromXml(xmlContents); if (newObject == null) { - // LoggingUtils.getLogger(). - // warning("Could not get object from XML."); return null; } @@ -172,30 +109,28 @@ public static T read(File file, ObjectXml stream) { } /** - * Generic implementation of read method, without user-defined mappings. - * - * @param file - * @param objectClass - * @return + * Reads an object from a file using a generic implementation without user-defined mappings. + * + * @param file the file to read from + * @param objectClass the class of the object + * @param the type of the object + * @return the deserialized object */ - // public static T read(File file, final Class objectClass) { public static T read(File file, final Class objectClass) { String contents = SpecsIo.read(file); return from(contents, objectClass); - /* - * //ObjectXml objXml = new ObjectXml() { ObjectXml objXml = new - * ObjectXml() { - * - * @Override public Class getTargetClass() { return objectClass; } }; - * - * Object anObj = read(file, objXml); return objectClass.cast(anObj); - * //return read(file, objXml); - */ } + /** + * Converts an XML string to an object of the specified class. + * + * @param contents the XML string + * @param objectClass the class of the object + * @param the type of the object + * @return the deserialized object + */ public static T from(String contents, final Class objectClass) { ObjectXml objXml = new ObjectXml() { - @Override public Class getTargetClass() { return objectClass; @@ -203,16 +138,13 @@ public Class getTargetClass() { }; return objXml.fromXml(contents); - /* - * Object anObj = objXml.fromXml(contents); - * - * return objectClass.cast(anObj); - */ } /** - * @param aspectDataFile - * @param aspectData + * Writes an object to a file. + * + * @param file the file to write to + * @param value the object to write */ public static void write(File file, Object value) { String xml = toString(value); @@ -220,10 +152,11 @@ public static void write(File file, Object value) { } /** - * Copies an object. - * - * @param object - * @return + * Copies an object by serializing and deserializing it. + * + * @param object the object to copy + * @param the type of the object + * @return a copy of the object */ @SuppressWarnings("unchecked") public static T copy(T object) { diff --git a/XStreamPlus/src/org/suikasoft/XStreamPlus/XmlPersistence.java b/XStreamPlus/src/org/suikasoft/XStreamPlus/XmlPersistence.java index a7f04f37..f5507261 100644 --- a/XStreamPlus/src/org/suikasoft/XStreamPlus/XmlPersistence.java +++ b/XStreamPlus/src/org/suikasoft/XStreamPlus/XmlPersistence.java @@ -1,12 +1,12 @@ /** * Copyright 2012 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -25,82 +25,73 @@ import pt.up.fe.specs.util.utilities.PersistenceFormat; /** - * @author Joao Bispo - * + * Implementation of {@link PersistenceFormat} for XML serialization using XStream. + * Allows registering custom ObjectXml mappings for specific classes. */ public class XmlPersistence extends PersistenceFormat { private final Map, ObjectXml> xmlObjects = SpecsFactory.newHashMap(); /** - * @param objectXmls + * Adds a list of ObjectXml mappings to this persistence instance. + * If a mapping for a class already exists, it will be replaced and a warning will be logged. + * + * @param objectXmls the list of ObjectXml mappings to add */ public void addObjectXml(List> objectXmls) { List> replacedClasses = new ArrayList<>(); - for (ObjectXml objectXml : objectXmls) { - // Check if mapping does not overlap with previous mapping if (xmlObjects.containsKey(objectXml.getTargetClass())) { replacedClasses.add(objectXml.getTargetClass()); } - - // Add class to map xmlObjects.put(objectXml.getTargetClass(), objectXml); } - - // Show warning message if (!replacedClasses.isEmpty()) { SpecsLogs.warn("Overlap in the following key mappings:" + replacedClasses); } - } - /* - * (non-Javadoc) - * - * @see - * org.specs.DymaLib.Graphs.Utils.PersistenceFormat.PersistenceFormat#to - * (java.lang.Object, java.lang.Object[]) + /** + * Serializes the given object to an XML string. + * + * @param anObject the object to serialize + * @return the XML string */ @Override public String to(Object anObject) { - - // Check if class of given object is in table ObjectXml objectXml = xmlObjects.get(anObject.getClass()); if (objectXml == null) { return XStreamUtils.toString(anObject); } - return objectXml.toXml(anObject); } - /* - * (non-Javadoc) - * - * @see - * org.specs.DymaLib.Graphs.Utils.PersistenceFormat.PersistenceFormat#from - * (java.lang.String, java.lang.Class, java.lang.Object[]) + /** + * Deserializes the given XML string to an object of the specified class. + * + * @param contents the XML string + * @param classOfObject the class to deserialize to + * @param the type of the object + * @return the deserialized object */ @Override public T from(String contents, Class classOfObject) { - - // Check if class of given object is in table ObjectXml objectXml = xmlObjects.get(classOfObject); - if (objectXml == null) { return XStreamUtils.from(contents, classOfObject); } - return classOfObject.cast(objectXml.fromXml(contents)); } - /* (non-Javadoc) - * @see pt.up.fe.specs.util.Utilities.PersistenceFormat#getExtension() + /** + * Returns the file extension for XML files. + * + * @return the string "xml" */ @Override public String getExtension() { - return "xml"; + return "xml"; } } diff --git a/XStreamPlus/src/org/suikasoft/XStreamPlus/XmlSerializable.java b/XStreamPlus/src/org/suikasoft/XStreamPlus/XmlSerializable.java index a667774b..925f5bf0 100644 --- a/XStreamPlus/src/org/suikasoft/XStreamPlus/XmlSerializable.java +++ b/XStreamPlus/src/org/suikasoft/XStreamPlus/XmlSerializable.java @@ -1,18 +1,17 @@ /* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * under the License. + * Copyright 2011 SPeCS Research Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.suikasoft.XStreamPlus; diff --git a/XStreamPlus/src/org/suikasoft/XStreamPlus/converters/OptionalConverter.java b/XStreamPlus/src/org/suikasoft/XStreamPlus/converters/OptionalConverter.java index 55e6f411..b5cb8432 100644 --- a/XStreamPlus/src/org/suikasoft/XStreamPlus/converters/OptionalConverter.java +++ b/XStreamPlus/src/org/suikasoft/XStreamPlus/converters/OptionalConverter.java @@ -1,11 +1,11 @@ /** * Copyright 2022 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -21,19 +21,33 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +/** + * XStream converter for serializing and deserializing {@link Optional} values. + */ public class OptionalConverter implements Converter { + /** + * Checks if this converter can handle the given type. + * + * @param type the class to check + * @return true if the type is Optional, false otherwise + */ @Override - public boolean canConvert(Class type) { + public boolean canConvert(@SuppressWarnings("rawtypes") Class type) { return type.equals(Optional.class); } + /** + * Serializes an Optional value to XML. + * + * @param source the source object + * @param writer the XML writer + * @param context the marshalling context + */ @Override public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { var optional = (Optional) source; - writer.addAttribute("isPresent", Boolean.toString(optional.isPresent())); - if (optional.isPresent()) { var value = optional.get(); writer.startNode("value"); @@ -41,18 +55,21 @@ public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingC context.convertAnother(value); writer.endNode(); } - } + /** + * Deserializes an Optional value from XML. + * + * @param reader the XML reader + * @param context the unmarshalling context + * @return the deserialized Optional value + */ @Override public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { - var isPresent = Boolean.parseBoolean(reader.getAttribute("isPresent")); - if (!isPresent) { return Optional.empty(); } - reader.moveDown(); var dummyOptional = Optional.of("dummy"); var classname = reader.getAttribute("classname"); @@ -64,7 +81,6 @@ public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext co } var value = context.convertAnother(dummyOptional, valueClass); reader.moveUp(); - return Optional.of(value); } diff --git a/jOptions/README.md b/jOptions/README.md new file mode 100644 index 00000000..1cbbd354 --- /dev/null +++ b/jOptions/README.md @@ -0,0 +1,15 @@ +# jOptions + +jOptions is a flexible Java library for managing configuration options, data stores, and tree-structured data. It provides utilities for encoding/decoding values, handling enums, and working with persistent and in-memory data representations. + +## Features +- Data store and configuration management +- Enum and value encoding/decoding utilities +- Tree node and property abstractions +- XML and other persistence formats + +## Usage +Add jOptions to your Java project to manage configuration options and data stores with advanced features. + +## License +This project is licensed under the Apache License 2.0. diff --git a/jOptions/src/org/suikasoft/GsonPlus/JsonStringList.java b/jOptions/src/org/suikasoft/GsonPlus/JsonStringList.java index f1a65bee..48989c84 100644 --- a/jOptions/src/org/suikasoft/GsonPlus/JsonStringList.java +++ b/jOptions/src/org/suikasoft/GsonPlus/JsonStringList.java @@ -1,14 +1,14 @@ /* * Copyright 2011 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.GsonPlus; @@ -18,17 +18,32 @@ import pt.up.fe.specs.util.exceptions.NotImplementedException; /** - * For compatibility with legacy Clava configuration files - * + * List implementation for compatibility with legacy Clava configuration files. + *

+ * This class is a placeholder and its methods are not implemented. + * * @author Joao Bispo */ public class JsonStringList extends AbstractList { + /** + * Not implemented. Throws exception if called. + * + * @param index the index of the element to return + * @return never returns normally + * @throws NotImplementedException always + */ @Override public String get(int index) { throw new NotImplementedException(this); } + /** + * Not implemented. Throws exception if called. + * + * @return never returns normally + * @throws NotImplementedException always + */ @Override public int size() { throw new NotImplementedException(this); diff --git a/jOptions/src/org/suikasoft/GsonPlus/JsonStringListXstreamConverter.java b/jOptions/src/org/suikasoft/GsonPlus/JsonStringListXstreamConverter.java index 85b7fe77..d8fe6273 100644 --- a/jOptions/src/org/suikasoft/GsonPlus/JsonStringListXstreamConverter.java +++ b/jOptions/src/org/suikasoft/GsonPlus/JsonStringListXstreamConverter.java @@ -1,14 +1,14 @@ /** * Copyright 2021 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.GsonPlus; @@ -24,27 +24,46 @@ import pt.up.fe.specs.util.utilities.StringList; /** - * For compatibility with legacy Clava configuration files - * - * @author JBispo + * XStream converter for {@link JsonStringList} for compatibility with legacy Clava configuration files. * + * @author JBispo */ public class JsonStringListXstreamConverter implements Converter { + /** + * Checks if the converter can handle the given type. + * + * @param type the class type to check + * @return true if the type is JsonStringList, false otherwise + */ + @SuppressWarnings("rawtypes") @Override public boolean canConvert(Class type) { return type.equals(JsonStringList.class); } + /** + * Not implemented. Throws exception if called. + * + * @param source the source object + * @param writer the writer + * @param context the context + * @throws RuntimeException always + */ @Override public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { throw new RuntimeException("This should not be used"); - } + /** + * Unmarshals a JsonStringList from XML. + * + * @param reader the XML reader + * @param context the context + * @return the unmarshalled object + */ @Override public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { - reader.moveDown(); // Collect list of strings @@ -54,13 +73,10 @@ public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext co reader.moveDown(); flags.add(reader.getValue()); reader.moveUp(); - } reader.moveUp(); return new StringList(flags); - } - } diff --git a/jOptions/src/org/suikasoft/jOptions/DataStore/ADataClass.java b/jOptions/src/org/suikasoft/jOptions/DataStore/ADataClass.java index 0719d230..7c20043a 100644 --- a/jOptions/src/org/suikasoft/jOptions/DataStore/ADataClass.java +++ b/jOptions/src/org/suikasoft/jOptions/DataStore/ADataClass.java @@ -27,60 +27,96 @@ import pt.up.fe.specs.util.SpecsLogs; import pt.up.fe.specs.util.providers.StringProvider; +/** + * Abstract base class for DataClass implementations. + * + *

This class provides a base implementation for data classes backed by a DataStore, supporting locking and common get/set operations. + * + * @param the type of the DataClass + */ public abstract class ADataClass> implements DataClass, StringProvider { private final DataStore data; private boolean isLocked; + /** + * Constructs an ADataClass backed by the given DataStore. + * + * @param data the DataStore backing this DataClass + */ public ADataClass(DataStore data) { this.data = data; this.isLocked = false; } + /** + * Constructs an ADataClass with a new DataStore based on the class interface. + */ public ADataClass() { - // this(DataStore.newInstance(getClass())); - // this.data = DataStore.newInstance(getClass()); - - // Cannot use previous Constructor because we cannot call 'getClass()' from whitin 'this(...)' this.data = DataStore.newInstance(StoreDefinitions.fromInterface(getClass()), false); } + /** + * Returns the backing DataStore. + * + * @return the DataStore backing this DataClass + */ protected DataStore getDataStore() { return data; } + /** + * Locks this DataClass, preventing further modifications. + * + * @return this instance, locked + */ @SuppressWarnings("unchecked") public T lock() { this.isLocked = true; return (T) this; } + /** + * Returns the name of this DataClass. + * + * @return the name of this DataClass + */ @Override public String getDataClassName() { return data.getName(); } + /** + * Returns an Optional containing the StoreDefinition, if present. + * + * @return an Optional with the StoreDefinition, or empty if not present + */ @Override public Optional getStoreDefinitionTry() { return data.getStoreDefinitionTry() .map(def -> new StoreDefinitionBuilder(getDataClassName()).addDefinition(def).build()); } - // public DataClass(T instance) { - // this.data = DataStore.newInstance(getClass()); - // this.data.addAll(((DataClass) instance).data); - // } - - /* (non-Javadoc) - * @see org.suikasoft.jOptions.DataStore.DataClass#get(org.suikasoft.jOptions.Datakey.DataKey) + /** + * Gets the value for the given DataKey. + * + * @param key the DataKey + * @param the value type + * @return the value for the key */ @Override public K get(DataKey key) { return data.get(key); } - /* (non-Javadoc) - * @see org.suikasoft.jOptions.DataStore.DataClass#set(org.suikasoft.jOptions.Datakey.DataKey, E) + /** + * Sets the value for the given DataKey. + * + * @param key the DataKey + * @param value the value to set + * @param the value type + * @param the value type (extends K) + * @return this instance */ @Override @SuppressWarnings("unchecked") @@ -93,8 +129,11 @@ public T set(DataKey key, E value) { return (T) this; } - /* (non-Javadoc) - * @see org.suikasoft.jOptions.DataStore.DataClass#set(T) + /** + * Sets all values from another DataClass instance. + * + * @param instance the instance to copy from + * @return this instance */ @Override @SuppressWarnings("unchecked") @@ -107,11 +146,23 @@ public T set(T instance) { return (T) this; } + /** + * Checks if the given DataKey has a value. + * + * @param key the DataKey + * @param the value type + * @return true if the key has a value, false otherwise + */ @Override public boolean hasValue(DataKey key) { return data.hasValue(key); } + /** + * Returns a collection of DataKeys that have values. + * + * @return a collection of DataKeys with values + */ @Override public Collection> getDataKeysWithValues() { StoreDefinition storeDefinition = data.getStoreDefinitionTry().get(); @@ -130,6 +181,11 @@ public Collection> getDataKeysWithValues() { return keysWithValues; } + /** + * Computes the hash code for this DataClass. + * + * @return the hash code + */ @Override public int hashCode() { final int prime = 31; @@ -142,6 +198,12 @@ public int hashCode() { return result; } + /** + * Checks if this DataClass is equal to another object. + * + * @param obj the object to compare + * @return true if equal, false otherwise + */ @Override public boolean equals(Object obj) { if (this == obj) @@ -169,24 +231,23 @@ public boolean equals(Object obj) { return true; } + /** + * Returns the string representation of this DataClass. + * + * @return the string representation + */ @Override public String getString() { return toString(); } + /** + * Returns the string representation of this DataClass. + * + * @return the string representation + */ @Override public String toString() { return toInlinedString(); } - - // @Override - // public DataStore getData() { - // return data; - // } - - /* - public DataStore getData() { - return data; - } - */ } diff --git a/jOptions/src/org/suikasoft/jOptions/DataStore/ADataStore.java b/jOptions/src/org/suikasoft/jOptions/DataStore/ADataStore.java index ddb29a59..37bed5f0 100644 --- a/jOptions/src/org/suikasoft/jOptions/DataStore/ADataStore.java +++ b/jOptions/src/org/suikasoft/jOptions/DataStore/ADataStore.java @@ -27,18 +27,27 @@ import pt.up.fe.specs.util.SpecsCheck; +/** + * Abstract base class for DataStore implementations. + * + *

This class provides a base implementation for DataStore, including value storage, definition management, and persistence support. + */ public abstract class ADataStore implements DataStore { - // private final SimpleSetup data; private final String name; private final Map values; private StoreDefinition definition; private AppPersistence persistence; private File configFile; - - // private SetupFile setupFile; private boolean strict; + /** + * Constructs an ADataStore with the given name, values, and store definition. + * + * @param name the name of the DataStore + * @param values the map of values + * @param definition the store definition + */ protected ADataStore(String name, Map values, StoreDefinition definition) { @@ -47,42 +56,40 @@ protected ADataStore(String name, Map values, if (!name.equals(definition.getName())) { definition = StoreDefinition.newInstance(name, definition.getKeys()); } - // Preconditions.checkArgument(name == definition.getName(), - // "Name of the DataStore (" + name + ") and of the definition (" + definition.getName() - // + ") do not agree"); } - // data = null; this.name = name; this.values = values; strict = false; this.definition = definition; - // setupFile = null; } + /** + * Constructs an ADataStore with the given name and another DataStore as source. + * + * @param name the name of the DataStore + * @param dataStore the source DataStore + */ public ADataStore(String name, DataStore dataStore) { - // public ADataStore(String name, StoreDefinition definition) { - // this(new SimpleSetup(name, dataStore), dataStore.getKeyMap()); - // this(name, new HashMap<>(dataStore.getValuesMap()), dataStore.getStoreDefinition().orElse(null)); this(name, new HashMap<>(), dataStore.getStoreDefinitionTry().orElse(null)); - - // this(name, new HashMap<>(), definition); - set(dataStore); - // data.setValues(dataStore); - // keys.putAll(dataStore.getKeyMap()); } + /** + * Constructs an ADataStore with the given StoreDefinition. + * + * @param storeDefinition the store definition + */ public ADataStore(StoreDefinition storeDefinition) { - // this(SimpleSetup.newInstance(storeDefinition), storeDefinition.getKeyMap()); - // this(storeDefinition.getName(), new LinkedHashMap<>(storeDefinition.getKeyMap()), new HashMap<>()); - // this(storeDefinition.getName(), storeDefinition.getKeyMap(), storeDefinition.getDefaultValues()); this(storeDefinition.getName(), new HashMap<>(), storeDefinition); } + /** + * Constructs an ADataStore with the given name. + * + * @param name the name of the DataStore + */ public ADataStore(String name) { - // this(new SimpleSetup(name), new HashMap<>()); - // this(name, new LinkedHashMap<>(), new HashMap<>()); this(name, new HashMap<>(), null); } @@ -98,7 +105,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { - // Compares name, values and string flag + // Compares name, values and strict flag if (this == obj) { return true; } @@ -144,28 +151,7 @@ public void setStoreDefinition(StoreDefinition definition) { this.definition = definition; } - /* - private ADataStore(SimpleSetup data, Map> keys) { - this.data = data; - this.keys = keys; - - setupFile = null; - - throw new RuntimeException("Do not use this version"); - } - */ - - /* - public ADataStore(String name, DataView setup) { - // this(new SimpleSetup(name, setup), setup.getKeyMap()); - - - // data.setValues(setup); - // keys.putAll(setup.getKeyMap()); - } - */ @Override - // public Optional set(DataKey key, E value) { public ADataStore set(DataKey key, E value) { SpecsCheck.checkNotNull(value, () -> "Tried to set a null value with key '" + key + "'. Use .remove() instead"); @@ -177,47 +163,15 @@ public ADataStore set(DataKey key, E value) { realValue = setter.get().get(value, this); } - // Do not replace key if it already exists - // if (!keys.containsKey(key.getName())) { - // keys.put(key.getName(), key); - // } - - // return data.setValue(key, value); - // Stop if value is not compatible with class of key if (key.verifyValueClass() && !key.getValueClass().isInstance(realValue)) { throw new RuntimeException("Tried to add a value of type '" + realValue.getClass() + "', with a key that supports '" + key.getValueClass() + "'"); - - // // Check if there is a common type, besides Object - // Class currentClass = key.getValueClass().getSuperclass(); - // - // boolean foundCommonType = false; - // while (!currentClass.equals(Object.class)) { - // foundCommonType = currentClass.isInstance(value); - // if (foundCommonType) { - // break; - // } - // currentClass = currentClass.getSuperclass(); - // } - // - // if (!foundCommonType) { - // throw new RuntimeException("Tried to add a value of type '" + value.getClass() - // + "', with a key that supports '" + key.getValueClass() + "'"); - // } - } - // Optional previousValue = setRaw(key.getName(), value); setRaw(key.getName(), realValue); return this; - - // if (!previousValue.isPresent()) { - // return Optional.empty(); - // } - // - // return Optional.of(key.getValueClass().cast(previousValue.get())); } @Override @@ -225,13 +179,6 @@ public Optional setRaw(String key, Object value) { return Optional.ofNullable(values.put(key, value)); } - // @Override - // public ADataStore set(DataStore setup) { - // values.putAll(setup.getValuesMap()); - // - // return this; - // } - @Override public Optional remove(DataKey key) { Optional value = getTry(key); @@ -241,11 +188,6 @@ public Optional remove(DataKey key) { return Optional.empty(); } - // Value is present, remove key and value from maps - // if (keys.remove(key.getName()) == null) { - // throw new RuntimeException("There was no key mapping for key '" + key + "'"); - // } - if (values.remove(key.getName()) == null) { throw new RuntimeException("There was no value mapping for key '" + key + "'"); } @@ -255,9 +197,7 @@ public Optional remove(DataKey key) { @Override public String getName() { - // return data.getName(); return getStoreDefinitionTry().map(def -> def.getName()).orElse(name); - // return name; } @Override @@ -268,16 +208,12 @@ public T get(DataKey key) { throw new RuntimeException( "No value present in DataStore '" + getName() + "' " + " for key '" + key.getName() + "'"); } - // DataKey storedKey = keys.get(key.getName()); - // if (strict && storedKey == null) { - // throw new RuntimeException("Key '" + key.getName() + "' is not present in DataStore '" + getName() + "'"); - // } T value = null; try { value = key.getValueClass().cast(valueRaw); } catch (Exception e) { - throw new RuntimeException("Could not retrive value from key " + key, e); + throw new RuntimeException("Could not retrieve value from key " + key, e); } // If value is null, use default value @@ -291,8 +227,6 @@ public T get(DataKey key) { values.put(key.getName(), value); } - // T value = data.getValue(key); - // Check if key has custom getter Optional> getter = key.getCustomGetter(); if (getter.isPresent()) { @@ -312,15 +246,6 @@ public boolean hasValue(DataKey key) { return values.get(key.getName()) != null; } - /** - * - */ - // @Override - // public Map getValuesMap() { - // // return new HashMap<>(values); - // return values; - // } - @Override public Collection getKeysWithValues() { return values.keySet(); @@ -329,21 +254,8 @@ public Collection getKeysWithValues() { @Override public String toString() { return toInlinedString(); - - /* - StringBuilder builder = new StringBuilder(); - builder.append("DataStore (" + getName()).append(")\n"); - for (String key : values.keySet()) { - builder.append(" - ").append(key).append(" : ").append(values.get(key)).append("\n"); - } - return builder.toString(); - */ } - // Object get(String id) { - // return getValuesMap().get(id); - // } - @Override public DataStore setPersistence(AppPersistence persistence) { this.persistence = persistence; diff --git a/jOptions/src/org/suikasoft/jOptions/DataStore/DataClass.java b/jOptions/src/org/suikasoft/jOptions/DataStore/DataClass.java index 5f3d7592..f8c6b8b1 100644 --- a/jOptions/src/org/suikasoft/jOptions/DataStore/DataClass.java +++ b/jOptions/src/org/suikasoft/jOptions/DataStore/DataClass.java @@ -23,24 +23,60 @@ import pt.up.fe.specs.util.SpecsNumbers; /** - * A class that replaces fields with public static DataKey instances. - * - * @author JoaoBispo + * Interface for classes that replace fields with public static DataKey instances. + * + *

This interface defines the contract for data classes that use DataKeys instead of fields, supporting get/set operations and store definition access. * - * @param + * @param the type of the DataClass */ public interface DataClass> { + /** + * Returns the name of this DataClass. + * + * @return the name of the DataClass + */ String getDataClassName(); + /** + * Gets the value for the given DataKey. + * + * @param key the DataKey + * @param the value type + * @return the value for the key + */ K get(DataKey key); + /** + * Sets the value for the given DataKey. + * + * @param key the DataKey + * @param value the value to set + * @param the value type + * @param the value type (extends K) + * @return this instance + */ T set(DataKey key, E value); + /** + * Sets a boolean DataKey to true. + * + * @param key the boolean DataKey + * @return this instance + */ default T set(DataKey key) { return set(key, true); } + /** + * Sets an Optional DataKey to the given value, or empty if value is null. + * + * @param key the Optional DataKey + * @param value the value to set + * @param the value type + * @param the value type (extends K) + * @return this instance + */ default T setOptional(DataKey> key, E value) { if (value == null) { return set(key, Optional.empty()); @@ -49,6 +85,12 @@ default T setOptional(DataKey> key, E value) { return set(key, Optional.of(value)); } + /** + * Gets the value for the given key name (String). + * + * @param key the key name + * @return the value for the key + */ default Object getValue(String key) { var def = getStoreDefinitionTry().orElseThrow( () -> new RuntimeException(".getValue() only supported if DataClass has a StoreDefinition")); @@ -58,6 +100,13 @@ default Object getValue(String key) { return get(datakey); } + /** + * Sets the value for the given key name (String). + * + * @param key the key name + * @param value the value to set + * @return the previous value + */ default Object setValue(String key, Object value) { var def = getStoreDefinitionTry().orElseThrow( () -> new RuntimeException(".setValue() only supported if DataClass has a StoreDefinition")); @@ -69,33 +118,44 @@ default Object setValue(String key, Object value) { } /** - * - * @return an Optional containing a StoreDefinition, if defined. By default returns empty. + * Returns an Optional containing the StoreDefinition, if defined. + * + * @return an Optional with the StoreDefinition, or empty if not present */ default Optional getStoreDefinitionTry() { return Optional.empty(); } + /** + * Returns the StoreDefinition, or throws if not defined. + * + * @return the StoreDefinition + */ default StoreDefinition getStoreDefinition() { return getStoreDefinitionTry().orElseThrow(() -> new RuntimeException("No StoreDefinition defined")); } - // default T set(DataKey> key, T value) { - // return set(key, Optional.of(value)); - // } - + /** + * Sets all values from another DataClass instance. + * + * @param instance the instance to copy from + * @return this instance + */ T set(T instance); /** - * - * @param key - * @return true, if it contains a non-null value for the given key, not considering default values + * Checks if this DataClass has a non-null value for the given key (not considering defaults). + * + * @param key the DataKey + * @param the value type + * @return true if a value is present, false otherwise */ boolean hasValue(DataKey key); /** - * - * @return All the keys that are mapped to a value + * Returns all DataKeys that are mapped to a value. + * + * @return a collection of DataKeys with values */ Collection> getDataKeysWithValues(); @@ -105,7 +165,7 @@ default StoreDefinition getStoreDefinition() { *

* By default, returns false. * - * @return + * @return true if the DataClass is closed, false otherwise */ default boolean isClosed() { return false; @@ -114,13 +174,10 @@ default boolean isClosed() { /** * Increments the value of the given key by one. * - * @param key - * @return + * @param key the DataKey + * @return the previous value */ default Number inc(DataKey key) { - // if (Integer.class.isAssignableFrom(key.getValueClass())) { - // return inc(key, (int) 1); - // } return inc(key, 1); } @@ -130,13 +187,14 @@ default Number inc(DataKey key) { *

* If there is not value for the given key, it is initialized to zero. * - * @param key - * @param amount - * @return + * @param key the DataKey + * @param amount the amount to increment + * @param the type of the key's value + * @param the type of the amount + * @return the previous value */ @SuppressWarnings("unchecked") default N1 inc(DataKey key, N2 amount) { - // Check if value is already present if (!hasValue(key)) { set(key, (N1) SpecsNumbers.zero(key.getValueClass())); } @@ -149,7 +207,7 @@ default N1 inc(DataKey key, N2 amount /** * Increments the value of all given keys by the amounts in the given DataClass. * - * @param dataClass + * @param dataClass the DataClass containing the keys and amounts */ @SuppressWarnings("unchecked") default void inc(DataClass dataClass) { @@ -164,27 +222,34 @@ default void inc(DataClass dataClass) { } } + /** + * Increments the value of the given integer key by one. + * + * @param key the DataKey + * @return the previous value + */ default Integer incInt(DataKey key) { return incInt(key, 1); } + /** + * Increments the value of the given integer key by the given amount. + * + * @param key the DataKey + * @param amount the amount to increment + * @return the previous value + */ default Integer incInt(DataKey key, int amount) { Integer previousValue = get(key); set(key, previousValue + amount); return previousValue; } - // default Integer inc(DataKey key, int amount) { - // // Check if value is already present - // if (!hasValue(key)) { - // set(key, 0); - // } - // - // Integer previousValue = get(key); - // set(key, previousValue + amount); - // return previousValue; - // } - + /** + * Returns a string representation of this DataClass in an inline format. + * + * @return the inline string representation + */ default String toInlinedString() { var keys = getDataKeysWithValues(); @@ -197,7 +262,6 @@ default String toInlinedString() { return keys.stream() .map(key -> key.getName() + ": " + DataClassUtils.toString(get(key))) .collect(Collectors.joining(", ", "[", "]")); - } /** @@ -206,29 +270,14 @@ default String toInlinedString() { *

* This function should be safe to use as long as the keys refer to immutable objects. * - * @param - * @param - * @param key - * @param source - * @return + * @param key the DataKey + * @param source the source DataClass + * @param the type of the key's value + * @param the type of the value (extends K) + * @return this instance */ default T copyValue(DataKey key, T source) { var value = source.get(key); - // Not many keys implement copy... - // // System.out.println("SOURCE: " + source.get(key)); - // if (key.getCopyFunction().isPresent()) { - // value = key.copy(value); - // SpecsLogs.info("Copy successful"); - // } else { - // SpecsLogs.info( - // "DataClass.copyValue: could not copy value of DataKey '" + key - // + "', using the original value"); - // } - return set(key, value); } - - // default List> getKeys() { - // return getStoreDefinition().getKeys(); - // } } \ No newline at end of file diff --git a/jOptions/src/org/suikasoft/jOptions/DataStore/DataClassUtils.java b/jOptions/src/org/suikasoft/jOptions/DataStore/DataClassUtils.java index 967abbc3..d28e8c44 100644 --- a/jOptions/src/org/suikasoft/jOptions/DataStore/DataClassUtils.java +++ b/jOptions/src/org/suikasoft/jOptions/DataStore/DataClassUtils.java @@ -20,17 +20,20 @@ import pt.up.fe.specs.util.providers.StringProvider; +/** + * Utility class for DataClass-related operations. + * + *

This class provides static methods for safely converting DataClass values to strings, handling cycles and common types. + */ public class DataClassUtils { /** * Properly converts to string the value of a DataClass. - * - *

- * Simply calling toString() on a DataClass value might cause infinite cycles, in case there are circular - * dependences. - * - * @param dataClassValue - * @return + * + *

Simply calling toString() on a DataClass value might cause infinite cycles, in case there are circular dependencies. + * + * @param dataClassValue the value to convert + * @return a string representation of the value */ public static String toString(Object dataClassValue) { if (dataClassValue instanceof StringProvider) { diff --git a/jOptions/src/org/suikasoft/jOptions/DataStore/DataClassWrapper.java b/jOptions/src/org/suikasoft/jOptions/DataStore/DataClassWrapper.java index 54054462..d25421d4 100644 --- a/jOptions/src/org/suikasoft/jOptions/DataStore/DataClassWrapper.java +++ b/jOptions/src/org/suikasoft/jOptions/DataStore/DataClassWrapper.java @@ -19,33 +19,76 @@ import org.suikasoft.jOptions.Datakey.DataKey; import org.suikasoft.jOptions.storedefinition.StoreDefinition; -// public class DataClassWrapper implements DataClass { +/** + * Abstract wrapper for DataClass implementations. + * + *

This class wraps another DataClass and delegates all operations to it, allowing extension or adaptation. + * + * @param the type of the DataClass + */ public abstract class DataClassWrapper> implements DataClass { private final DataClass data; + /** + * Constructs a DataClassWrapper for the given DataClass. + * + * @param data the DataClass to wrap + */ public DataClassWrapper(DataClass data) { this.data = data; } + /** + * Returns the wrapped DataClass instance. + * + * @return the wrapped DataClass + */ protected abstract T getThis(); + /** + * Returns the name of the wrapped DataClass. + * + * @return the name of the wrapped DataClass + */ @Override public String getDataClassName() { return data.getDataClassName(); } + /** + * Retrieves the value associated with the given key from the wrapped DataClass. + * + * @param the type of the value + * @param key the key to retrieve the value for + * @return the value associated with the key + */ @Override public K get(DataKey key) { return data.get(key); } + /** + * Sets the value for the given key in the wrapped DataClass. + * + * @param the type of the value + * @param the type of the value to set + * @param key the key to set the value for + * @param value the value to set + * @return the current instance + */ @Override public T set(DataKey key, E value) { data.set(key, value); return getThis(); } + /** + * Sets all values from the given instance into the wrapped DataClass. + * + * @param instance the instance to copy values from + * @return the current instance + */ @Override public T set(T instance) { for (var key : instance.getDataKeysWithValues()) { @@ -56,26 +99,53 @@ public T set(T instance) { return getThis(); } + /** + * Checks if the wrapped DataClass has a value for the given key. + * + * @param the type of the value + * @param key the key to check + * @return true if the wrapped DataClass has a value for the key, false otherwise + */ @Override public boolean hasValue(DataKey key) { return data.hasValue(key); } + /** + * Retrieves all keys with values from the wrapped DataClass. + * + * @return a collection of keys with values + */ @Override public Collection> getDataKeysWithValues() { return data.getDataKeysWithValues(); } + /** + * Attempts to retrieve the store definition of the wrapped DataClass. + * + * @return an optional containing the store definition, or empty if not available + */ @Override public Optional getStoreDefinitionTry() { return data.getStoreDefinitionTry(); } + /** + * Checks if the wrapped DataClass is closed. + * + * @return true if the wrapped DataClass is closed, false otherwise + */ @Override public boolean isClosed() { return data.isClosed(); } + /** + * Returns a string representation of the wrapped DataClass. + * + * @return a string representation of the wrapped DataClass + */ @Override public String toString() { return toInlinedString(); diff --git a/jOptions/src/org/suikasoft/jOptions/DataStore/DataKeyProvider.java b/jOptions/src/org/suikasoft/jOptions/DataStore/DataKeyProvider.java index a61da816..1e38a82a 100644 --- a/jOptions/src/org/suikasoft/jOptions/DataStore/DataKeyProvider.java +++ b/jOptions/src/org/suikasoft/jOptions/DataStore/DataKeyProvider.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.DataStore; @@ -16,27 +16,15 @@ import org.suikasoft.jOptions.Datakey.DataKey; /** - * Returns a DataKey. - * - * @author JoaoBispo + * Interface for classes that provide data keys. * + *

This interface defines a contract for providing a DataKey instance. */ public interface DataKeyProvider { - + /** + * Returns the data key associated with this provider. + * + * @return the data key + */ DataKey getDataKey(); - - // & DataKeyProvider> Class getEnumClass(); - // Class getEnumClass(); - - // - // @Override - // default StoreDefinition getStoreDefinition() { - // - // List> keys = new ArrayList<>(); - // for (T enumValue : getEnumClass().getEnumConstants()) { - // keys.add(enumValue.getDataKey()); - // } - // - // return StoreDefinition.newInstance(getEnumClass().getSimpleName(), keys); - // } } diff --git a/jOptions/src/org/suikasoft/jOptions/DataStore/DataStoreContainer.java b/jOptions/src/org/suikasoft/jOptions/DataStore/DataStoreContainer.java index a09055a8..f65ab925 100644 --- a/jOptions/src/org/suikasoft/jOptions/DataStore/DataStoreContainer.java +++ b/jOptions/src/org/suikasoft/jOptions/DataStore/DataStoreContainer.java @@ -16,17 +16,14 @@ import org.suikasoft.jOptions.Interfaces.DataStore; /** - * Contains a DataStore. - *

- * TODO: Move DefaultCleanSetup to here, make this interface package-private - * - * @author JoaoBispo + * Interface for classes that contain a DataStore. * + *

This interface provides a method to retrieve the contained DataStore instance. */ public interface DataStoreContainer { - /** - * + * Returns the contained DataStore instance. + * * @return a DataStore */ DataStore getDataStore(); diff --git a/jOptions/src/org/suikasoft/jOptions/DataStore/EnumDataKeyProvider.java b/jOptions/src/org/suikasoft/jOptions/DataStore/EnumDataKeyProvider.java index a6068ee8..8ac4ca6c 100644 --- a/jOptions/src/org/suikasoft/jOptions/DataStore/EnumDataKeyProvider.java +++ b/jOptions/src/org/suikasoft/jOptions/DataStore/EnumDataKeyProvider.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.DataStore; @@ -20,31 +20,45 @@ import org.suikasoft.jOptions.storedefinition.StoreDefinition; import org.suikasoft.jOptions.storedefinition.StoreDefinitionProvider; +/** + * Interface for enums that provide a DataKey and a StoreDefinition. + * + *

This interface is designed for enums that need to provide data keys and store definitions in a type-safe manner. It combines the functionality of {@link DataKeyProvider} and {@link StoreDefinitionProvider}. + * + * @param the type of the enum implementing this interface + */ public interface EnumDataKeyProvider & EnumDataKeyProvider> extends DataKeyProvider, StoreDefinitionProvider { + /** + * Returns the DataKey associated with this enum constant. + * + * @return the DataKey associated with the enum constant + */ @Override DataKey getDataKey(); - // & DataKeyProvider> Class getEnumClass(); + /** + * Returns the class of the enum implementing this interface. + * + * @return the enum class + */ Class getEnumClass(); - /* - default & DataKeyProvider> StoreDefinition getStoreDefinition() { - for (T enumConstant : getEnumClass().getEnumConstants()) { - - } - } - */ + /** + * Returns the StoreDefinition for the enum implementing this interface. + * + *

The StoreDefinition contains all {@link DataKey}s provided by the enum constants. This method aggregates all data keys from the enum constants into a single store definition. + * + * @return the StoreDefinition for the enum + */ @Override default StoreDefinition getStoreDefinition() { - - List> keys = new ArrayList<>(); - for (T enumValue : getEnumClass().getEnumConstants()) { - keys.add(enumValue.getDataKey()); - } - - return StoreDefinition.newInstance(getEnumClass().getSimpleName(), keys); + List> keys = new ArrayList<>(); + for (T enumValue : getEnumClass().getEnumConstants()) { + keys.add(enumValue.getDataKey()); + } + return StoreDefinition.newInstance(getEnumClass().getSimpleName(), keys); } } diff --git a/jOptions/src/org/suikasoft/jOptions/DataStore/GenericDataClass.java b/jOptions/src/org/suikasoft/jOptions/DataStore/GenericDataClass.java index 72fb942e..48dc8aea 100644 --- a/jOptions/src/org/suikasoft/jOptions/DataStore/GenericDataClass.java +++ b/jOptions/src/org/suikasoft/jOptions/DataStore/GenericDataClass.java @@ -1,24 +1,34 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.DataStore; import org.suikasoft.jOptions.Interfaces.DataStore; +/** + * Generic implementation of a DataClass backed by a DataStore. + * + *

This class provides a generic DataClass implementation that delegates to a DataStore. + * + * @param the type of the DataClass + */ public class GenericDataClass> extends ADataClass { - + /** + * Constructs a GenericDataClass with the given DataStore. + * + * @param data the DataStore backing this DataClass + */ public GenericDataClass(DataStore data) { super(data); } - } diff --git a/jOptions/src/org/suikasoft/jOptions/DataStore/ListDataStore.java b/jOptions/src/org/suikasoft/jOptions/DataStore/ListDataStore.java index 6613a81e..411ed5a2 100644 --- a/jOptions/src/org/suikasoft/jOptions/DataStore/ListDataStore.java +++ b/jOptions/src/org/suikasoft/jOptions/DataStore/ListDataStore.java @@ -30,12 +30,10 @@ /** * Implementation of DataStore that uses a List to store the data. - * - *

- * This implementation requires a StoreDefinition. - * - * @author JoaoBispo * + *

This implementation requires a StoreDefinition and stores values in a list indexed by the definition. + * + * @author JoaoBispo */ public class ListDataStore implements DataStore { @@ -46,18 +44,23 @@ public class ListDataStore implements DataStore { private boolean strict; + /** + * Constructs a ListDataStore with the given StoreDefinition. + * + * @param keys the StoreDefinition defining the keys + */ public ListDataStore(StoreDefinition keys) { this.keys = keys; this.values = new ArrayList<>(keys.getKeys().size()); - // Fill array with nulls - // for (int i = 0; i < keys.getKeys().size(); i++) { - // this.values.add(null); - // } - this.strict = false; } + /** + * Ensures the internal list has enough size to accommodate the given index. + * + * @param index the index to ensure size for + */ private void ensureSize(int index) { if (index < values.size()) { return; @@ -69,11 +72,24 @@ private void ensureSize(int index) { } } + /** + * Retrieves the value at the given index. + * + * @param index the index to retrieve the value from + * @return the value at the given index + */ private Object get(int index) { ensureSize(index); return values.get(index); } + /** + * Sets the value at the given index. + * + * @param index the index to set the value at + * @param value the value to set + * @return the previous value at the given index + */ private Object set(int index, Object value) { ensureSize(index); return values.set(index, value); @@ -110,6 +126,15 @@ public boolean equals(Object obj) { return true; } + /** + * Sets the value for the given DataKey. + * + * @param the type of the value + * @param the type of the value to set + * @param key the DataKey to set the value for + * @param value the value to set + * @return the current DataStore instance + */ @Override public DataStore set(DataKey key, E value) { SpecsCheck.checkNotNull(value, () -> "Tried to set a null value with key '" + key + "'. Use .remove() instead"); @@ -125,6 +150,13 @@ public DataStore set(DataKey key, E value) { return this; } + /** + * Sets the raw value for the given key. + * + * @param key the key to set the value for + * @param value the value to set + * @return an Optional containing the previous value, or empty if the key does not exist + */ @Override public Optional setRaw(String key, Object value) { // Do not set key @@ -136,32 +168,57 @@ public Optional setRaw(String key, Object value) { return Optional.ofNullable(set(index, value)); } + /** + * Sets whether the DataStore operates in strict mode. + * + * @param value true to enable strict mode, false otherwise + */ @Override public void setStrict(boolean value) { this.strict = value; } + /** + * Retrieves the StoreDefinition associated with this DataStore. + * + * @return an Optional containing the StoreDefinition + */ @Override public Optional getStoreDefinitionTry() { return Optional.of(keys); } + /** + * Sets the StoreDefinition for this DataStore. + * + * @param definition the StoreDefinition to set + * @throws RuntimeException if called, as this implementation does not support setting the StoreDefinition after instantiation + */ @Override public void setStoreDefinition(StoreDefinition definition) { throw new RuntimeException( "This implementation does not support setting the StoreDefinition after instantiation"); } + /** + * Retrieves the name of this DataStore. + * + * @return the name of the DataStore + */ @Override public String getName() { return keys.getName(); } + /** + * Retrieves the value for the given DataKey. + * + * @param the type of the value + * @param key the DataKey to retrieve the value for + * @return the value associated with the DataKey + */ @Override public T get(DataKey key) { - - // boolean hasKey = keys.hasKey(key.getName()); - // Object valueRaw = hasKey ? values.get(toIndex(key)) : null; Object valueRaw = get(toIndex(key)); if (strict && valueRaw == null) { throw new RuntimeException( @@ -181,19 +238,11 @@ public T get(DataKey key) { throw new RuntimeException("No default value for key '" + key.getName() + "' in this object: " + this); } - // if (!defaultValue.isPresent()) { - // throw new RuntimeException("No default value for key '" + key.getName() + "' in this object: " + this); - // } - Optional defaultValue = key.getDefault(); value = defaultValue.orElse(null); // Storing value, in case it is a mutable value (e.g., a list) - // if (hasKey) { setRaw(key.getName(), value); - // } - - // values.put(key.getName(), value); } // Check if key has custom getter @@ -205,6 +254,13 @@ public T get(DataKey key) { return value; } + /** + * Removes the value associated with the given DataKey. + * + * @param the type of the value + * @param key the DataKey to remove the value for + * @return an Optional containing the removed value, or empty if no value was present + */ @Override public Optional remove(DataKey key) { Optional value = getTry(key); @@ -215,27 +271,27 @@ public Optional remove(DataKey key) { } set(toIndex(key), null); - // if (values.remove(toIndex(key)) != value.get()) { - // throw new RuntimeException("Removed wrong value"); - // } return value; } + /** + * Checks if the given DataKey has an associated value. + * + * @param the type of the value + * @param key the DataKey to check + * @return true if the DataKey has an associated value, false otherwise + */ @Override public boolean hasValue(DataKey key) { - // System.out.println("VALUES:" + values); - // System.out.println("KEY:" + key); - // System.out.println("KEy INDEX:" + toIndex(key)); - - // Check if it has the index - // if (!getIndexes().hasIndex(key)) { - // return false; - // } - return get(toIndex(key)) != null; } + /** + * Retrieves the keys that have associated values. + * + * @return a collection of keys with values + */ @Override public Collection getKeysWithValues() { List keysWithValues = new ArrayList<>(); @@ -248,16 +304,33 @@ public Collection getKeysWithValues() { return keysWithValues; } + /** + * Converts the given DataKey to its index in the StoreDefinition. + * + * @param key the DataKey to convert + * @return the index of the DataKey + */ private int toIndex(DataKey key) { return toIndex(key.getName()); } + /** + * Converts the given key name to its index in the StoreDefinition. + * + * @param key the key name to convert + * @return the index of the key name + */ private int toIndex(String key) { StoreDefinitionIndexes indexes = getIndexes(); return indexes.getIndex(key); } + /** + * Retrieves the StoreDefinitionIndexes for the current StoreDefinition. + * + * @return the StoreDefinitionIndexes + */ private StoreDefinitionIndexes getIndexes() { StoreDefinitionIndexes indexes = KEY_TO_INDEXES.get(keys); if (indexes == null) { @@ -267,13 +340,21 @@ private StoreDefinitionIndexes getIndexes() { return indexes; } + /** + * Retrieves the value associated with the given key name. + * + * @param id the key name to retrieve the value for + * @return the value associated with the key name + */ @Override public Object get(String id) { return get(toIndex(id)); } /** - * This implementation is always closed. + * Checks if this DataStore is closed. + * + * @return true, as this implementation is always closed */ @Override public boolean isClosed() { diff --git a/jOptions/src/org/suikasoft/jOptions/DataStore/SimpleDataStore.java b/jOptions/src/org/suikasoft/jOptions/DataStore/SimpleDataStore.java index 49cd66e8..033152c1 100644 --- a/jOptions/src/org/suikasoft/jOptions/DataStore/SimpleDataStore.java +++ b/jOptions/src/org/suikasoft/jOptions/DataStore/SimpleDataStore.java @@ -16,37 +16,37 @@ import org.suikasoft.jOptions.Interfaces.DataStore; import org.suikasoft.jOptions.storedefinition.StoreDefinition; +/** + * Simple implementation of a DataStore. + * + *

This class provides a basic DataStore backed by a map, supporting construction from a name, another DataStore, or a StoreDefinition. + */ public class SimpleDataStore extends ADataStore { + /** + * Constructs a SimpleDataStore with the given name. + * + * @param name the name of the DataStore + */ public SimpleDataStore(String name) { super(name); } + /** + * Constructs a SimpleDataStore with the given name and another DataStore as source. + * + * @param name the name of the DataStore + * @param dataStore the source DataStore + */ public SimpleDataStore(String name, DataStore dataStore) { super(name, dataStore); - // super(name, dataStore.getStoreDefinition().orElse(null)); - /* - // Add values - Optional storeDefinition = dataStore.getStoreDefinition(); - if (!storeDefinition.isPresent()) { - LoggingUtils.msgInfo("StoreDefinition is not present, doing raw copy without key information"); - setValuesMap(getValues()); - return; - } - - for (DataKey key : storeDefinition.get().getKeys()) { - Object value = dataStore.get(key); - setRaw(key, value); - } - */ - } - - /* - public SimpleDataStore(String name, DataView setup) { - super(name, setup); } - */ + /** + * Constructs a SimpleDataStore with the given StoreDefinition. + * + * @param storeDefinition the store definition + */ public SimpleDataStore(StoreDefinition storeDefinition) { super(storeDefinition); } diff --git a/jOptions/src/org/suikasoft/jOptions/Datakey/ADataKey.java b/jOptions/src/org/suikasoft/jOptions/Datakey/ADataKey.java index 9a113934..514a0366 100644 --- a/jOptions/src/org/suikasoft/jOptions/Datakey/ADataKey.java +++ b/jOptions/src/org/suikasoft/jOptions/Datakey/ADataKey.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Datakey; @@ -24,6 +24,13 @@ import pt.up.fe.specs.util.exceptions.NotImplementedException; import pt.up.fe.specs.util.parsing.StringCodec; +/** + * Abstract base class for {@link DataKey} implementations. + * + *

This class provides the foundational implementation for data keys, including support for default values, decoders, custom getters/setters, and extra data. + * + * @param the type of value associated with this key + */ public abstract class ADataKey implements DataKey { private final String id; @@ -37,6 +44,20 @@ public abstract class ADataKey implements DataKey { private transient final CustomGetter customSetter; private transient final DataKeyExtraData extraData; + /** + * Constructs an instance of {@code ADataKey} with the specified parameters. + * + * @param id the unique identifier for this key + * @param defaultValueProvider a supplier for the default value of this key + * @param decoder a codec for encoding and decoding values + * @param customGetter a custom getter for retrieving values + * @param panelProvider a provider for GUI panels associated with this key + * @param label a label for this key + * @param definition the store definition associated with this key + * @param copyFunction a function for copying values + * @param customSetter a custom setter for setting values + * @param extraData additional data associated with this key + */ protected ADataKey(String id, Supplier defaultValueProvider, StringCodec decoder, CustomGetter customGetter, KeyPanelProvider panelProvider, String label, StoreDefinition definition, Function copyFunction, CustomGetter customSetter, @@ -56,15 +77,31 @@ protected ADataKey(String id, Supplier defaultValueProvider, String this.extraData = extraData; } + /** + * Constructs an instance of {@code ADataKey} with the specified identifier and default value provider. + * + * @param id the unique identifier for this key + * @param defaultValue a supplier for the default value of this key + */ protected ADataKey(String id, Supplier defaultValue) { this(id, defaultValue, null, null, null, null, null, null, null, null); } + /** + * Returns the name of this key. + * + * @return the name of this key + */ @Override public String getName() { return id; } + /** + * Returns a string representation of this key. + * + * @return a string representation of this key + */ @Override public String toString() { return DataKey.toString(this); @@ -101,23 +138,45 @@ public boolean equals(Object obj) { return true; } + /** + * Creates a copy of this {@code DataKey} with the specified parameters. + * + * @param id the unique identifier for the new key + * @param defaultValueProvider a supplier for the default value of the new key + * @param decoder a codec for encoding and decoding values + * @param customGetter a custom getter for retrieving values + * @param panelProvider a provider for GUI panels associated with the new key + * @param label a label for the new key + * @param definition the store definition associated with the new key + * @param copyFunction a function for copying values + * @param customSetter a custom setter for setting values + * @param extraData additional data associated with the new key + * @return a new {@code DataKey} instance + */ abstract protected DataKey copy(String id, Supplier defaultValueProvider, StringCodec decoder, CustomGetter customGetter, KeyPanelProvider panelProvider, String label, StoreDefinition definition, Function copyFunction, CustomGetter customSetter, DataKeyExtraData extraData); + /** + * Returns the decoder associated with this key, if present. + * + * @return an {@code Optional} containing the decoder, or an empty {@code Optional} if no decoder is set + */ @Override public Optional> getDecoder() { return Optional.ofNullable(decoder); } + /** + * Sets the decoder for this key. + * + * @param decoder the new decoder + * @return a new {@code DataKey} instance with the updated decoder + */ @Override public DataKey setDecoder(StringCodec decoder) { - - // Adding interface 'Serializable', so that it can save lambda expressions StringCodec serializableDecoder = getSerializableDecoder(decoder); - // StringCodec serializableDecoder = (StringCodec & Serializable) value -> decoder.decode(value); - // return copy(id, defaultValueProvider, serializableDecoder, customGetter, return copy(id, defaultValueProvider, serializableDecoder, customGetter, panelProvider, label, definition, copyFunction, customSetter, extraData); } @@ -128,9 +187,13 @@ private static StringCodec getSerializableDecoder(StringCodec decoder) } return StringCodec.newInstance(decoder::encode, decoder::decode); - // return (StringCodec & Serializable) value -> decoder.decode(value); } + /** + * Returns the default value for this key, if present. + * + * @return an {@code Optional} containing the default value, or an empty {@code Optional} if no default value is set + */ @Override public Optional getDefault() { if (defaultValueProvider != null) { @@ -140,11 +203,22 @@ public Optional getDefault() { return Optional.empty(); } + /** + * Checks if this key has a default value. + * + * @return {@code true} if this key has a default value, {@code false} otherwise + */ @Override public boolean hasDefaultValue() { return defaultValueProvider != null; } + /** + * Sets the default value provider for this key. + * + * @param defaultValueProvider the new default value provider + * @return a new {@code DataKey} instance with the updated default value provider + */ @Override public DataKey setDefault(Supplier defaultValueProvider) { return copy(id, defaultValueProvider, decoder, customGetter, panelProvider, label, definition, copyFunction, @@ -159,9 +233,14 @@ public DataKey setDefaultRaw(Supplier defaultValueProvider) { customSetter, extraData); } + /** + * Sets a custom getter for this key. + * + * @param customGetter the new custom getter + * @return a new {@code DataKey} instance with the updated custom getter + */ @Override public DataKey setCustomGetter(CustomGetter customGetter) { - // Adding interface 'Serializable', so that it can save lambda expressions @SuppressWarnings("unchecked") CustomGetter serializableGetter = (CustomGetter & Serializable) (value, dataStore) -> customGetter .get(value, dataStore); @@ -170,9 +249,14 @@ public DataKey setCustomGetter(CustomGetter customGetter) { copyFunction, customSetter, extraData); } + /** + * Sets a custom setter for this key. + * + * @param customSetter the new custom setter + * @return a new {@code DataKey} instance with the updated custom setter + */ @Override public DataKey setCustomSetter(CustomGetter customSetter) { - // Adding interface 'Serializable', so that it can save lambda expressions @SuppressWarnings("unchecked") CustomGetter serializableSetter = (CustomGetter & Serializable) (value, dataStore) -> customSetter .get(value, dataStore); @@ -181,27 +265,54 @@ public DataKey setCustomSetter(CustomGetter customSetter) { copyFunction, serializableSetter, extraData); } + /** + * Returns the custom getter associated with this key, if present. + * + * @return an {@code Optional} containing the custom getter, or an empty {@code Optional} if no custom getter is set + */ @Override public Optional> getCustomGetter() { return Optional.ofNullable(customGetter); } + /** + * Returns the custom setter associated with this key, if present. + * + * @return an {@code Optional} containing the custom setter, or an empty {@code Optional} if no custom setter is set + */ @Override public Optional> getCustomSetter() { return Optional.ofNullable(customSetter); } + /** + * Sets the panel provider for this key. + * + * @param panelProvider the new panel provider + * @return a new {@code DataKey} instance with the updated panel provider + */ @Override public DataKey setKeyPanelProvider(KeyPanelProvider panelProvider) { return copy(id, defaultValueProvider, decoder, customGetter, panelProvider, label, definition, copyFunction, customSetter, extraData); } + /** + * Returns the panel provider associated with this key, if present. + * + * @return an {@code Optional} containing the panel provider, or an empty {@code Optional} if no panel provider is set + */ @Override public Optional> getKeyPanelProvider() { return Optional.ofNullable(panelProvider); } + /** + * Sets the label for this key. + * + * @param label the new label + * @return a new {@code DataKey} instance with the updated label + */ @Override public DataKey setLabel(String label) { return copy(id, defaultValueProvider, decoder, customGetter, panelProvider, label, definition, copyFunction, @@ -209,9 +320,9 @@ public DataKey setLabel(String label) { } /** - * As default, returns the name of the key if a label is not set. + * Returns the label for this key. If no label is set, returns the name of the key. * - * @return + * @return the label for this key */ @Override public String getLabel() { @@ -222,17 +333,34 @@ public String getLabel() { return label; } + /** + * Sets the store definition for this key. + * + * @param definition the new store definition + * @return a new {@code DataKey} instance with the updated store definition + */ @Override public DataKey setStoreDefinition(StoreDefinition definition) { return copy(id, defaultValueProvider, decoder, customGetter, panelProvider, label, definition, copyFunction, customSetter, extraData); } + /** + * Returns the store definition associated with this key, if present. + * + * @return an {@code Optional} containing the store definition, or an empty {@code Optional} if no store definition is set + */ @Override public Optional getStoreDefinition() { return Optional.ofNullable(definition); } + /** + * Sets the copy function for this key. + * + * @param copyFunction the new copy function + * @return a new {@code DataKey} instance with the updated copy function + */ @Override public DataKey setCopyFunction(Function copyFunction) { return copy(id, defaultValueProvider, decoder, customGetter, panelProvider, label, definition, copyFunction, @@ -244,14 +372,16 @@ public DataKey setValueClass(Class valueClass) { throw new NotImplementedException(this); } + /** + * Returns the copy function associated with this key, if present. If no copy function is set, uses the encoder/decoder by default. + * + * @return an {@code Optional} containing the copy function, or an empty {@code Optional} if no copy function is set + */ @Override public Optional> getCopyFunction() { - // By default, use encoder/decoder if (copyFunction == null && getDecoder().isPresent()) { var codec = getDecoder().get(); Function copy = value -> ADataKey.copy(value, codec); - // Function copy = value -> codec.decode(codec.encode(value)); - // copyFunction = copy; return Optional.of(copy); } @@ -265,11 +395,22 @@ private static T copy(T value, StringCodec codec) { return decodedValue; } + /** + * Returns the extra data associated with this key, if present. + * + * @return an {@code Optional} containing the extra data, or an empty {@code Optional} if no extra data is set + */ @Override public Optional getExtraData() { return Optional.ofNullable(extraData); } + /** + * Sets the extra data for this key. + * + * @param extraData the new extra data + * @return a new {@code DataKey} instance with the updated extra data + */ @Override public DataKey setExtraData(DataKeyExtraData extraData) { return copy(id, defaultValueProvider, decoder, customGetter, panelProvider, label, definition, copyFunction, diff --git a/jOptions/src/org/suikasoft/jOptions/Datakey/Codecs.java b/jOptions/src/org/suikasoft/jOptions/Datakey/Codecs.java index d310f303..9c733f5d 100644 --- a/jOptions/src/org/suikasoft/jOptions/Datakey/Codecs.java +++ b/jOptions/src/org/suikasoft/jOptions/Datakey/Codecs.java @@ -1,14 +1,14 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Datakey; @@ -24,26 +24,50 @@ import pt.up.fe.specs.util.collections.MultiMap; import pt.up.fe.specs.util.parsing.StringCodec; +/** + * Utility class for common {@link pt.up.fe.specs.util.parsing.StringCodec} implementations for DataKey types. + * + *

This class provides static methods to create codecs for common types such as File and Map. + */ public class Codecs { private static final String FILES_WITH_BASE_FOLDER_SEPARATOR = ";"; + /** + * Creates a {@link StringCodec} for {@link File} objects. + * + * @return a codec for encoding and decoding {@link File} objects + */ public static StringCodec file() { - // Function encoder = f -> SpecsIo.normalizePath(f); Function decoder = s -> s == null ? new File("") : new File(s); - return StringCodec.newInstance(Codecs::fileEncoder, decoder); } + /** + * Encodes a {@link File} object into a normalized path string. + * + * @param file the file to encode + * @return the normalized path string + */ private static String fileEncoder(File file) { - System.out.println("ENCODING FILE:" + SpecsIo.normalizePath(file)); return SpecsIo.normalizePath(file); } + /** + * Creates a {@link StringCodec} for mapping {@link File} objects to their base folders. + * + * @return a codec for encoding and decoding mappings of files to base folders + */ public static StringCodec> filesWithBaseFolders() { return StringCodec.newInstance(Codecs::filesWithBaseFoldersEncoder, Codecs::filesWithBaseFoldersDecoder); } + /** + * Decodes a string representation of file-to-base-folder mappings into a {@link Map}. + * + * @param value the string representation of the mappings + * @return a map of files to their base folders + */ private static Map filesWithBaseFoldersDecoder(String value) { MultiMap basesToPaths = SpecsStrings.parsePathList(value, FILES_WITH_BASE_FOLDER_SEPARATOR); @@ -52,7 +76,6 @@ private static Map filesWithBaseFoldersDecoder(String value) { for (String base : basesToPaths.keySet()) { var paths = basesToPaths.get(base); - // For base folder File baseFolder = base.isEmpty() ? null : new File(base); for (String path : paths) { @@ -63,22 +86,22 @@ private static Map filesWithBaseFoldersDecoder(String value) { return filesWithBases; } + /** + * Encodes a map of files to their base folders into a string representation. + * + * @param value the map of files to base folders + * @return the string representation of the mappings + */ private static String filesWithBaseFoldersEncoder(Map value) { - MultiMap basesToPaths = new MultiMap<>(); for (var entry : value.entrySet()) { - String base = entry.getValue() == null ? "" : entry.getValue().toString(); - String path = base.isEmpty() ? entry.getKey().toString() : SpecsIo.removeCommonPath(entry.getKey(), entry.getValue()).toString(); - // var baseFile = entry.getValue(); - // var pathFile = entry.getKey(); basesToPaths.add(base, path); } - // Special case: only one empty base folder if (basesToPaths.size() == 1 && basesToPaths.containsKey("")) { return basesToPaths.get("").stream().collect(Collectors.joining()); } @@ -88,19 +111,11 @@ private static String filesWithBaseFoldersEncoder(Map value) { .collect(Collectors.joining(FILES_WITH_BASE_FOLDER_SEPARATOR)); String pathsWithPrefix = basesToPaths.entrySet().stream() - // Ignore empty key .filter(entry -> !entry.getKey().isEmpty()) .map(entry -> "$" + entry.getKey() + "$" + entry.getValue().stream().collect(Collectors.joining(FILES_WITH_BASE_FOLDER_SEPARATOR))) .collect(Collectors.joining()); return pathsNoPrefix + pathsWithPrefix; - // value.entrySet().stream() - // .map(entry -> entry.getValue() == null ? "$$" : "$" + entry.getValue() + "$" + entry.getKey()) - // .collect(Collectors.joining(FILES_WITH_BASE_FOLDER_SEPARATOR)); - // - // return value.entrySet().stream() - // .map(entry -> entry.getValue() == null ? "$$" : "$" + entry.getValue() + "$" + entry.getKey()) - // .collect(Collectors.joining(FILES_WITH_BASE_FOLDER_SEPARATOR)); } } diff --git a/jOptions/src/org/suikasoft/jOptions/Datakey/CustomGetter.java b/jOptions/src/org/suikasoft/jOptions/Datakey/CustomGetter.java index 4ef9909c..85b6ee69 100644 --- a/jOptions/src/org/suikasoft/jOptions/Datakey/CustomGetter.java +++ b/jOptions/src/org/suikasoft/jOptions/Datakey/CustomGetter.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Datakey; @@ -16,11 +16,20 @@ import org.suikasoft.jOptions.Interfaces.DataStore; /** - * - * @author JoaoBispo + * Functional interface for custom value retrieval from a {@link org.suikasoft.jOptions.Interfaces.DataStore}. + * + *

Implement this interface to provide custom logic for retrieving values from a DataStore. * + * @param the type of value */ @FunctionalInterface public interface CustomGetter { + /** + * Returns a value for the given key and DataStore. + * + * @param value the current value + * @param dataStore the DataStore + * @return the value to use + */ T get(T value, DataStore dataStore); } diff --git a/jOptions/src/org/suikasoft/jOptions/Datakey/DataKey.java b/jOptions/src/org/suikasoft/jOptions/Datakey/DataKey.java index 163dd712..ac502e34 100644 --- a/jOptions/src/org/suikasoft/jOptions/Datakey/DataKey.java +++ b/jOptions/src/org/suikasoft/jOptions/Datakey/DataKey.java @@ -1,14 +1,14 @@ /** * Copyright 2014 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Datakey; @@ -29,94 +29,134 @@ import pt.up.fe.specs.util.utilities.StringLines; /** - * Keys for values with an associated type. - *

- * DataKey equality is done based only on the string name. - * + * Keys for values with an associated type. DataKey equality is based only on the string name. + * + *

This interface defines the contract for keys that are associated with a value type, including methods for retrieving the key name, value class, decoder, and for copying or setting properties. + * + * @param the type of value associated with this key * @see KeyFactory */ public interface DataKey extends KeyProvider { + /** + * Retrieves the key name. + * + * @return the name of the key + */ @Override default String getKey() { return getName(); } /** - * TODO: Rename to getType - * - * @return + * Retrieves the class type of the value associated with this key. + * + * @return the class type of the value */ Class getValueClass(); /** - * - * @param valueClass - * @return + * Sets the class type of the value associated with this key. + * + * @param valueClass the class type to set + * @return the updated DataKey instance */ DataKey setValueClass(Class valueClass); + /** + * Retrieves the name of the key. + * + * @return the name of the key + */ String getName(); + /** + * Retrieves the simple name of the class type of the value associated with this key. + * + * @return the simple name of the class type + */ default String getTypeName() { return getValueClass().getSimpleName(); } - /* - * DECODER - */ /** - * - * @return + * Retrieves the optional decoder for this key. + * + * @return an Optional containing the decoder, if present */ Optional> getDecoder(); /** - * A copy of this key, with decoder set. - * - * @param decoder - * @return + * Creates a copy of this key with the specified decoder. + * + * @param decoder the decoder to set + * @return the updated DataKey instance */ DataKey setDecoder(StringCodec decoder); + /** + * Decodes the given encoded value using the decoder associated with this key. + * + * @param encodedValue the encoded value to decode + * @return the decoded value + * @throws RuntimeException if no decoder is set + */ default T decode(String encodedValue) { return getDecoder() .map(codec -> codec.decode(encodedValue)) .orElseThrow(() -> new RuntimeException("No encoder/decoder set")); } + /** + * Encodes the given value using the decoder associated with this key. + * + * @param value the value to encode + * @return the encoded value + * @throws RuntimeException if no decoder is set + */ default String encode(T value) { return getDecoder() .map(codec -> codec.encode(value)) .orElseThrow(() -> new RuntimeException("No encoder/decoder set")); } - /* - * DEFAULT VALUE - */ /** - * - * @return + * Retrieves the optional default value for this key. + * + * @return an Optional containing the default value, if present */ Optional getDefault(); + /** + * Checks if this key has a default value. + * + * @return true if a default value is present, false otherwise + */ boolean hasDefaultValue(); - // default boolean hasDefaultValue() { - // return getDefault().isPresent(); - // } - /** - * - * A copy of this key, with a Supplier for the default value. - * - * @param defaultValue - * @return + * Creates a copy of this key with the specified default value supplier. + * + * @param defaultValue the supplier for the default value + * @return the updated DataKey instance */ DataKey setDefault(Supplier defaultValue); + /** + * Creates a copy of this key with the specified raw default value supplier. + * + * @param defaultValue the raw supplier for the default value + * @return the updated DataKey instance + */ DataKey setDefaultRaw(Supplier defaultValue); + /** + * Creates a copy of this key with the specified default value as a string. + * + * @param stringValue the default value as a string + * @return the updated DataKey instance + * @throws RuntimeException if no decoder is set + */ default DataKey setDefaultString(String stringValue) { if (!getDecoder().isPresent()) { throw new RuntimeException("Can only use this method if a decoder was set before"); @@ -125,77 +165,115 @@ default DataKey setDefaultString(String stringValue) { return this.setDefault(() -> getDecoder().get().decode(stringValue)); } - /* - * CUSTOM GETTER + /** + * Creates a copy of this key with the specified custom getter. + * + * @param defaultValue the custom getter to set + * @return the updated DataKey instance */ DataKey setCustomGetter(CustomGetter defaultValue); + /** + * Retrieves the optional custom getter for this key. + * + * @return an Optional containing the custom getter, if present + */ Optional> getCustomGetter(); - /* - * CUSTOM SETTER + /** + * Creates a copy of this key with the specified custom setter. + * + * @param setProcessing the custom setter to set + * @return the updated DataKey instance */ - DataKey setCustomSetter(CustomGetter setProcessing); + /** + * Retrieves the optional custom setter for this key. + * + * @return an Optional containing the custom setter, if present + */ Optional> getCustomSetter(); - /* - * KEY PANEL + /** + * Creates a copy of this key with the specified key panel provider. + * + * @param panelProvider the key panel provider to set + * @return the updated DataKey instance */ DataKey setKeyPanelProvider(KeyPanelProvider panelProvider); + /** + * Retrieves the optional key panel provider for this key. + * + * @return an Optional containing the key panel provider, if present + */ Optional> getKeyPanelProvider(); + /** + * Retrieves the key panel for this key using the given data store. + * + * @param data the data store to use + * @return the key panel + * @throws RuntimeException if no panel provider is defined + */ default KeyPanel getPanel(DataStore data) { - return getKeyPanelProvider() .orElseThrow(() -> new RuntimeException( "No panel defined for key '" + getName() + "' of type '" + getValueClass() + "'")) .getPanel(this, data); } - /* - * STORE DEFINITION - * - * TODO: Check if this is really needed + /** + * Creates a copy of this key with the specified store definition. + * + * @param definition the store definition to set + * @return the updated DataKey instance */ DataKey setStoreDefinition(StoreDefinition definition); - Optional getStoreDefinition(); - /** - * Helper method which guarantees type safety. - * - * @param data + * Retrieves the optional store definition for this key. + * + * @return an Optional containing the store definition, if present */ - /* - default void updatePanel(DataStore data) { - getPanel().setValue(data.get(this)); - } - */ + Optional getStoreDefinition(); - /* - * LABEL + /** + * Creates a copy of this key with the specified label. + * + * @param label the label to set + * @return the updated DataKey instance */ DataKey setLabel(String label); + /** + * Retrieves the label for this key. + * + * @return the label + */ String getLabel(); - /* - * EXTRA DATA + /** + * Retrieves the optional extra data for this key. + * + * @return an Optional containing the extra data, if present */ Optional getExtraData(); + /** + * Creates a copy of this key with the specified extra data. + * + * @param extraData the extra data to set + * @return the updated DataKey instance + */ DataKey setExtraData(DataKeyExtraData extraData); - /* - * TO STRING - */ /** - * - * @param key - * @return + * Converts the given key to a string representation. + * + * @param key the key to convert + * @return the string representation of the key */ static String toString(DataKey key) { StringBuilder builder = new StringBuilder(); @@ -210,7 +288,6 @@ static String toString(DataKey key) { if (value instanceof DataStore) { DataStore dataStoreValue = (DataStore) value; if (dataStoreValue.getStoreDefinitionTry().isPresent()) { - // Close parenthesis builder.append(")"); String dataStoreString = DataKey.toString(dataStoreValue.getStoreDefinitionTry().get().getKeys()); @@ -218,12 +295,10 @@ static String toString(DataKey key) { builder.append("\n").append(" ").append(line); } } else { - // Just close parenthesis, not definition of keys for this DataStore builder.append(" - Undefined DataStore)"); } } else { - // Append default value only if it only occupies one line String defaultValueString = defaultValue.toString(); if (StringLines.getLines(defaultValueString).size() == 1) { builder.append(" = ").append(defaultValue).append(")"); @@ -239,6 +314,12 @@ static String toString(DataKey key) { return builder.toString(); } + /** + * Converts the given collection of keys to a string representation. + * + * @param keys the collection of keys to convert + * @return the string representation of the keys + */ static String toString(Collection> keys) { StringBuilder builder = new StringBuilder(); @@ -253,51 +334,58 @@ static String toString(Collection> keys) { /** * Copies the given object. - * + * *

* 1) Uses the defined copy function to copy the object;
* 2) Returns the object itself (shallow copy). - * - * @return + * + * @param object the object to copy + * @return the copied object */ - // @SuppressWarnings("unchecked") // Should be ok default T copy(T object) { return getCopyFunction() .map(copyFunction -> copyFunction.apply(object)) .orElse(object); - // .orElse(object instanceof Copyable ? (T) ((Copyable) object).copy() : object); } + /** + * Copies the given object as a raw value. + * + * @param object the object to copy + * @return the copied object + */ default Object copyRaw(Object object) { return copy(getValueClass().cast(object)); - // return getCopyFunction() - // .map(copy -> copy.apply(getValueClass().cast(object))) - // .orElse(SpecsSystem.copy(getValueClass().cast(object))); } /** - * A copy of this key, with decoder set. - * - * @param decoder - * @return + * Creates a copy of this key with the specified copy function. + * + * @param copyFunction the copy function to set + * @return the updated DataKey instance */ DataKey setCopyFunction(Function copyFunction); + /** + * Retrieves the optional copy function for this key. + * + * @return an Optional containing the copy function, if present + */ Optional> getCopyFunction(); + /** + * Creates a copy of this key with a default copy constructor. + * + * @return the updated DataKey instance + */ default DataKey setCopyConstructor() { return setCopyFunction(object -> SpecsSystem.copy(object)); } /** - * If true, verifies if the class of a value being set is compatible with the value class of the key. - * - *

- * By default returns true. - * - * TODO: Replace with a "customValueVerification" - * - * @return + * Checks if the class of a value being set is compatible with the value class of the key. + * + * @return true if the class is compatible, false otherwise */ default boolean verifyValueClass() { return true; diff --git a/jOptions/src/org/suikasoft/jOptions/Datakey/DataKeyExtraData.java b/jOptions/src/org/suikasoft/jOptions/Datakey/DataKeyExtraData.java index 56918307..5b24fe27 100644 --- a/jOptions/src/org/suikasoft/jOptions/Datakey/DataKeyExtraData.java +++ b/jOptions/src/org/suikasoft/jOptions/Datakey/DataKeyExtraData.java @@ -1,20 +1,25 @@ /** * Copyright 2019 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Datakey; import org.suikasoft.jOptions.DataStore.ADataClass; +/** + * Extra data container for {@link org.suikasoft.jOptions.Datakey.DataKey} instances. + * + *

This class can be used to store additional metadata or configuration for a DataKey. + */ public class DataKeyExtraData extends ADataClass { } diff --git a/jOptions/src/org/suikasoft/jOptions/Datakey/GenericKey.java b/jOptions/src/org/suikasoft/jOptions/Datakey/GenericKey.java index 6cb0ec43..042ad9ab 100644 --- a/jOptions/src/org/suikasoft/jOptions/Datakey/GenericKey.java +++ b/jOptions/src/org/suikasoft/jOptions/Datakey/GenericKey.java @@ -8,7 +8,7 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Datakey; @@ -22,49 +22,85 @@ import pt.up.fe.specs.util.parsing.StringCodec; /** - * Implementation of DataKey that supports types with generics. + * Implementation of {@link DataKey} that supports types with generics. * - * @author JoaoBispo + *

This class allows the creation of data keys for values with generic types, using an example instance to infer the value class. * - * @param + * @param the type of value associated with this key */ public class GenericKey extends ADataKey { + /** + * Example instance of the value type, used for class inference. + */ private final T exampleInstance; + /** + * Cached value class, may be set explicitly or inferred from the example instance. + */ private transient Class valueClass = null; /** + * Constructs a GenericKey with the given id, example instance, and default value provider. * - * @param id - * @param aClass - * @param defaultValue + * @param id the key id + * @param exampleInstance an example instance of the value type + * @param defaultValue the default value provider */ public GenericKey(String id, T exampleInstance, Supplier defaultValue) { this(id, exampleInstance, defaultValue, null, null, null, null, null, null, null, null); } + /** + * Constructs a GenericKey with the given id and example instance. The default value provider returns null. + * + * @param id the key id + * @param exampleInstance an example instance of the value type + */ public GenericKey(String id, T exampleInstance) { this(id, exampleInstance, () -> null); } + /** + * Full constructor for GenericKey with all options. + * + * @param id the key id + * @param exampleInstance an example instance of the value type + * @param defaultValueProvider the default value provider + * @param decoder the string decoder/encoder for the value type + * @param customGetter a custom getter for the value + * @param panelProvider provider for UI panels + * @param label a label for the key + * @param definition the store definition + * @param copyFunction function to copy values + * @param customSetter a custom setter for the value + * @param extraData extra data for the key + */ protected GenericKey(String id, T exampleInstance, Supplier defaultValueProvider, StringCodec decoder, CustomGetter customGetter, KeyPanelProvider panelProvider, String label, StoreDefinition definition, Function copyFunction, CustomGetter customSetter, DataKeyExtraData extraData) { - super(id, defaultValueProvider, decoder, customGetter, panelProvider, label, definition, copyFunction, customSetter, extraData); - this.exampleInstance = exampleInstance; } - // 'exampleInstance' should return the correct class, the check has been done in the constructor + /** + * Returns the class of the value type. If not explicitly set, it is inferred from the example instance. + * + * @return the value class + */ @SuppressWarnings("unchecked") @Override public Class getValueClass() { return valueClass != null ? valueClass : (Class) exampleInstance.getClass(); } + /** + * Sets the value class explicitly. + * + * @param valueClass the class to set + * @return this GenericKey instance + */ @SuppressWarnings("unchecked") @Override public DataKey setValueClass(Class valueClass) { @@ -72,12 +108,26 @@ public DataKey setValueClass(Class valueClass) { return this; } + /** + * Creates a copy of this key with the given parameters. + * + * @param id the key id + * @param defaultValueProvider the default value provider + * @param decoder the string decoder/encoder + * @param customGetter a custom getter + * @param panelProvider provider for UI panels + * @param label a label for the key + * @param definition the store definition + * @param copyFunction function to copy values + * @param customSetter a custom setter + * @param extraData extra data for the key + * @return a new GenericKey instance + */ @Override protected DataKey copy(String id, Supplier defaultValueProvider, StringCodec decoder, CustomGetter customGetter, KeyPanelProvider panelProvider, String label, StoreDefinition definition, Function copyFunction, CustomGetter customSetter, DataKeyExtraData extraData) { - return new GenericKey<>(id, this.exampleInstance, defaultValueProvider, decoder, customGetter, panelProvider, label, definition, copyFunction, customSetter, extraData); } @@ -85,7 +135,8 @@ protected DataKey copy(String id, Supplier defaultValueProvider, /** * Due to the way Java implements generics, it is not possible to verify if a value is compatible based only on the * class of the example instance. - * + * + * @return false always, as generic type checking is not possible at runtime */ @Override public boolean verifyValueClass() { diff --git a/jOptions/src/org/suikasoft/jOptions/Datakey/KeyFactory.java b/jOptions/src/org/suikasoft/jOptions/Datakey/KeyFactory.java index 7e56dca5..a17be24c 100644 --- a/jOptions/src/org/suikasoft/jOptions/Datakey/KeyFactory.java +++ b/jOptions/src/org/suikasoft/jOptions/Datakey/KeyFactory.java @@ -1,14 +1,14 @@ /** * Copyright 2014 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Datakey; @@ -58,13 +58,18 @@ import pt.up.fe.specs.util.parsing.StringCodec; import pt.up.fe.specs.util.utilities.StringList; +/** + * Factory for creating common {@link DataKey} types and utility methods for key construction. + * + *

This class provides static methods to create DataKey instances for common types such as Boolean, String, Integer, and more. + */ public class KeyFactory { /** - * A Boolean DataKey, with default value 'false'. - * - * @param id - * @return + * Creates a Boolean {@link DataKey} with a default value of 'false'. + * + * @param id the identifier for the key + * @return a {@link DataKey} for Boolean values */ public static DataKey bool(String id) { return new NormalKey<>(id, Boolean.class) @@ -74,90 +79,140 @@ public static DataKey bool(String id) { } /** - * A String DataKey, without default value. - * - * @param id - * @return + * Creates a String {@link DataKey} without a default value. + * + * @param id the identifier for the key + * @return a {@link DataKey} for String values */ public static DataKey string(String id) { return new NormalKey<>(id, String.class) .setKeyPanelProvider((key, data) -> new StringPanel(key, data)) .setDecoder(s -> s) .setDefault(() -> ""); - } + /** + * Creates a String {@link DataKey} with a specified default value. + * + * @param id the identifier for the key + * @param defaultValue the default value for the key + * @return a {@link DataKey} for String values + */ public static DataKey string(String id, String defaultValue) { return string(id).setDefault(() -> defaultValue); } + /** + * Creates an Integer {@link DataKey} with a specified default value. + * + * @param id the identifier for the key + * @param defaultValue the default value for the key + * @return a {@link DataKey} for Integer values + */ public static DataKey integer(String id, int defaultValue) { return integer(id).setDefault(() -> defaultValue); } + /** + * Creates an Integer {@link DataKey} without a default value. + * + * @param id the identifier for the key + * @return a {@link DataKey} for Integer values + */ public static DataKey integer(String id) { return new NormalKey<>(id, Integer.class) .setKeyPanelProvider((key, data) -> new IntegerPanel(key, data)) .setDecoder(s -> SpecsStrings.decodeInteger(s, () -> 0)) .setDefault(() -> 0); - } + /** + * Creates a Long {@link DataKey} with a specified default value. + * + * @param id the identifier for the key + * @param defaultValue the default value for the key + * @return a {@link DataKey} for Long values + */ public static DataKey longInt(String id, long defaultValue) { return longInt(id) .setDefault(() -> defaultValue); } + /** + * Creates a Long {@link DataKey} without a default value. + * + * @param id the identifier for the key + * @return a {@link DataKey} for Long values + */ public static DataKey longInt(String id) { return new NormalKey<>(id, Long.class) - // .setDefault(() -> defaultValue) - // .setKeyPanelProvider((key, data) -> new IntegerPanel(key, data)) - .setDecoder(s -> SpecsStrings.decodeLong(s, () -> 0l)); - + .setDecoder(s -> SpecsStrings.decodeLong(s, () -> 0L)); } + /** + * Creates a Double {@link DataKey} with a specified default value. + * + * @param id the identifier for the key + * @param defaultValue the default value for the key + * @return a {@link DataKey} for Double values + */ public static DataKey double64(String id, double defaultValue) { return double64(id).setDefault(() -> defaultValue); } + /** + * Creates a Double {@link DataKey} without a default value. + * + * @param id the identifier for the key + * @return a {@link DataKey} for Double values + */ public static DataKey double64(String id) { return new NormalKey<>(id, Double.class) - // .setDefault(() -> defaultValue) .setKeyPanelProvider((key, data) -> new DoublePanel(key, data)) .setDecoder(s -> Double.valueOf(s)); - } + /** + * Creates a BigInteger {@link DataKey}. + * + * @param id the identifier for the key + * @return a {@link DataKey} for BigInteger values + */ public static DataKey bigInteger(String id) { return new NormalKey<>(id, BigInteger.class) .setDecoder(s -> new BigInteger(s)); } /** - * Helper method which returns a key for a file that does not have to exist. - * - * @param id - * @return + * Creates a {@link DataKey} for a file that does not have to exist. + * + * @param id the identifier for the key + * @return a {@link DataKey} for File values */ public static DataKey file(String id) { return file(id, false, false, false, Collections.emptyList()); } + /** + * Creates a {@link DataKey} for a file with specific extensions. + * + * @param id the identifier for the key + * @param extensions the allowed extensions for the file + * @return a {@link DataKey} for File values + */ public static DataKey file(String id, String... extensions) { return file(id, false, false, false, Arrays.asList(extensions)); } /** - * A File DataKey, with default value file with current path (.). - * - *

- * If 'isFolder' is true, it will try to create the folder when returning the File instance, even if it does not - * exist. - * - * @param id - * @param isFolder - * @param create - * @return + * Creates a {@link DataKey} for a file with various options. + * + * @param id the identifier for the key + * @param isFolder whether the file is a folder + * @param create whether to create the file if it does not exist + * @param exists whether the file must exist + * @param extensions the allowed extensions for the file + * @return a {@link DataKey} for File values */ private static DataKey file(String id, boolean isFolder, boolean create, boolean exists, Collection extensions) { @@ -174,12 +229,24 @@ private static DataKey file(String id, boolean isFolder, boolean create, b .setCustomGetter(customGetterFile(isFolder, !isFolder, create, exists)); } + /** + * Creates a {@link DataKey} for a path that can be either a file or a folder. + * + * @param id the identifier for the key + * @return a {@link DataKey} for File values + */ public static DataKey path(String id) { return path(id, false); } + /** + * Creates a {@link DataKey} for a path that can be either a file or a folder, with an option to check existence. + * + * @param id the identifier for the key + * @param exists whether the path must exist + * @return a {@link DataKey} for File values + */ public static DataKey path(String id, boolean exists) { - int fileChooser = JFileChooser.FILES_AND_DIRECTORIES; return new NormalKey<>(id, File.class, () -> new File("")) @@ -188,54 +255,58 @@ public static DataKey path(String id, boolean exists) { .setCustomGetter(customGetterFile(true, true, false, exists)); } + /** + * Custom getter for file paths, with options for folders, files, creation, and existence checks. + * + * @param canBeFolder whether the path can be a folder + * @param canBeFile whether the path can be a file + * @param create whether to create the path if it does not exist + * @param exists whether the path must exist + * @return a custom getter for file paths + */ public static CustomGetter customGetterFile(boolean canBeFolder, boolean canBeFile, boolean create, boolean exists) { - - // Normalize path before returning return (file, dataStore) -> new File( SpecsIo.normalizePath(customGetterFile(file, dataStore, canBeFolder, canBeFile, create, exists))); } + /** + * Processes file paths with options for folders, files, creation, and existence checks. + * + * @param file the file to process + * @param dataStore the data store containing additional information + * @param isFolder whether the path is a folder + * @param isFile whether the path is a file + * @param create whether to create the path if it does not exist + * @param exists whether the path must exist + * @return the processed file + */ public static File customGetterFile(File file, DataStore dataStore, boolean isFolder, boolean isFile, boolean create, boolean exists) { - - // If an empty path, return an empty path if (file.getPath().isEmpty() && !isFolder && isFile && !create) { return file; } File currentFile = file; - // If it has a working folder set var workingFolder = dataStore.get(JOptionKeys.CURRENT_FOLDER_PATH); - // if (!workingFolder.isEmpty()) { if (workingFolder.isPresent()) { - - // If path is not absolute, create new file with working folder as parent if (!currentFile.isAbsolute()) { - // File parentFolder = new File(workingFolder); File parentFolder = new File(workingFolder.get()); currentFile = new File(parentFolder, currentFile.getPath()); } - } currentFile = processPath(isFolder, isFile, create, currentFile); - // Check if it exists - if (exists) { - if (!currentFile.exists()) { - throw new RuntimeException("Path '" + currentFile + "' does not exist"); - } + if (exists && !currentFile.exists()) { + throw new RuntimeException("Path '" + currentFile + "' does not exist"); } - // If relative paths is enabled, make relative path with working folder. - // if (!workingFolder.isEmpty() && dataStore.get(JOptionKeys.USE_RELATIVE_PATHS)) { if (workingFolder.isPresent() && dataStore.get(JOptionKeys.USE_RELATIVE_PATHS)) { currentFile = new File(SpecsIo.getRelativePath(currentFile, new File(workingFolder.get()))); } - // if (!dataStore.get(JOptionKeys.USE_RELATIVE_PATHS) && !workingFolder.isEmpty()) { if (!dataStore.get(JOptionKeys.USE_RELATIVE_PATHS) && workingFolder.isPresent()) { currentFile = SpecsIo.getCanonicalFile(currentFile); } @@ -243,125 +314,123 @@ public static File customGetterFile(File file, DataStore dataStore, boolean isFo return currentFile; } + /** + * Processes the path with options for folders, files, and creation. + * + * @param canBeFolder whether the path can be a folder + * @param canBeFile whether the path can be a file + * @param create whether to create the path if it does not exist + * @param currentFile the current file to process + * @return the processed file + */ private static File processPath(boolean canBeFolder, boolean canBeFile, boolean create, File currentFile) { - var exists = currentFile.exists(); - if(!exists) { - - if(canBeFolder && create) { + if (!exists) { + if (canBeFolder && create) { return SpecsIo.mkdir(currentFile); } - return currentFile; } - if(currentFile.isDirectory() && !canBeFolder) { + if (currentFile.isDirectory() && !canBeFolder) { throw new RuntimeException("File key has directory as value and key does not allow it: '" + currentFile.getPath() + "')"); } - if(currentFile.isFile() && !canBeFile) { + if (currentFile.isFile() && !canBeFile) { throw new RuntimeException("File key has file as value and key does not allow it: '" + currentFile.getPath() + "')"); } - return currentFile; } /** - * Creates a key of type StringList, with default value being an empty list. + * Creates a {@link DataKey} for a {@link StringList} with an empty list as the default value. + * + * @param id the identifier for the key + * @return a {@link DataKey} for StringList values */ public static DataKey stringList(String id) { return stringList(id, Collections.emptyList()); } /** - * A generic DataKey without default value. - * - * @param id - * @param aClass - * @return + * Creates a generic {@link DataKey} without a default value. + * + * @param id the identifier for the key + * @param aClass the class of the key's value + * @return a {@link DataKey} for the specified type */ public static DataKey object(String id, Class aClass) { return new NormalKey<>(id, aClass); } - @SuppressWarnings("unchecked") // It is optional T, because of type erasure + /** + * Creates an optional {@link DataKey} with an empty optional as the default value. + * + * @param id the identifier for the key + * @return a {@link DataKey} for Optional values + */ + @SuppressWarnings("unchecked") public static DataKey> optional(String id) { return generic(id, (Optional) Optional.empty()) .setDefault(() -> Optional.empty()); } /** - * A StringList DataKey which has predefined values for the GUI element. - * - * @param string - * @return - */ - // public static DataKey stringListWithPredefinedValues(String id, List predefinedLabelValues) { - // - // return new NormalKey<>(id, StringList.class) - // .setDefault(() -> new StringList(Collections.emptyList())) - // .setDecoder(StringList.getCodec()) - // .setKeyPanelProvider((key, data) -> StringListPanel.newInstance(key, data, predefinedLabelValues)); - // } - - /** - * A new OptionDefinition, using a converter with the default separator (;) - * - * @param string - * @return + * Creates a {@link DataKey} for a {@link StringList} with predefined values. + * + * @param id the identifier for the key + * @param defaultValue the default value for the key + * @return a {@link DataKey} for StringList values */ public static DataKey stringList(String id, List defaultValue) { - return new NormalKey<>(id, StringList.class) .setDefault(() -> new StringList(defaultValue)) - // .setDecoder(value -> new StringList(value)) .setDecoder(StringList.getCodec()) .setKeyPanelProvider(StringListPanel::newInstance); } + /** + * Creates a {@link DataKey} for a {@link StringList} with predefined values. + * + * @param optionName the identifier for the key + * @param defaultValues the default values for the key + * @return a {@link DataKey} for StringList values + */ public static DataKey stringList(String optionName, String... defaultValues) { return stringList(optionName, Arrays.asList(defaultValues)); } /** - * TODO: Can be an interesting exercise to see if it pays off to use a class such as FileList that inside uses other - * keys. - * - * @param optionName - * @param extension - * @return + * Creates a {@link DataKey} for a {@link FileList}. + * + * @param optionName the identifier for the key + * @return a {@link DataKey} for FileList values */ - public static DataKey fileList(String optionName, String extension) { - return fileList(optionName); - } - public static DataKey fileList(String optionName) { - return KeyFactory.object(optionName, FileList.class).setDefault(() -> new FileList()) .setStoreDefinition(FileList.getStoreDefinition()) .setDecoder(FileList::decode); } /** - * - * - * @param id - * @return + * Creates a {@link DataKey} for a folder. + * + * @param id the identifier for the key + * @return a {@link DataKey} for File values */ public static DataKey folder(String id) { return file(id, true, false, false, Collections.emptyList()); } /** - * Creates a key for a folder that must exist. If the given folder does not exist when returning the value, throws - * an exception. - * - * @param id - * @return + * Creates a {@link DataKey} for an existing folder. + * + * @param id the identifier for the key + * @return a {@link DataKey} for File values */ public static DataKey existingFolder(String id) { return file(id, true, false, true, Collections.emptyList()) @@ -369,31 +438,36 @@ public static DataKey existingFolder(String id) { } /** - * Creates a key for a folder, sets './' as default value. - * - * @param id - * @param create - * if true, creates the path if it does not exist - * @return + * Creates a {@link DataKey} for a folder, with an option to create the folder if it does not exist. + * + * @param id the identifier for the key + * @param create whether to create the folder if it does not exist + * @return a {@link DataKey} for File values */ public static DataKey folder(String id, boolean create) { return KeyFactory.file(id, true, create, false, Collections.emptyList()) .setDefault(() -> new File("./")); - } - // public static DataKey folder(String id, boolean create, String defaultValue) { - // - // return file(id, true, create, Collections.emptyList()) - // .setDefault(new File(defaultValue)); - // - // } - + /** + * Creates a {@link DataKey} for a {@link SetupList}. + * + * @param id the identifier for the key + * @param definitions the store definitions for the setup list + * @return a {@link DataKey} for SetupList values + */ public static DataKey setupList(String id, List definitions) { return object(id, SetupList.class).setDefault(() -> SetupList.newInstance(id, new ArrayList<>(definitions))) .setKeyPanelProvider((key, data) -> new SetupListPanel(key, data, definitions)); } + /** + * Creates a {@link DataKey} for a {@link SetupList} using store definition providers. + * + * @param id the identifier for the key + * @param providers the store definition providers for the setup list + * @return a {@link DataKey} for SetupList values + */ public static DataKey setupList(String id, StoreDefinitionProvider... providers) { List definitions = new ArrayList<>(); @@ -404,48 +478,49 @@ public static DataKey setupList(String id, StoreDefinitionProvider... return setupList(id, definitions); } + /** + * Creates a {@link DataKey} for a {@link DataStore}. + * + * @param id the identifier for the key + * @param definition the store definition for the data store + * @return a {@link DataKey} for DataStore values + */ public static DataKey dataStore(String id, StoreDefinition definition) { - return object(id, DataStore.class) .setStoreDefinition(definition) - // .setDecoder(s -> { - // throw new RuntimeException("No decoder for DataStore"); - // }); .setDecoder(string -> KeyFactory.dataStoreDecoder(string, definition)); } + /** + * Decodes a {@link DataStore} from a string representation. + * + * @param string the string representation of the data store + * @param definition the store definition for the data store + * @return the decoded {@link DataStore} + */ private static DataStore dataStoreDecoder(String string, StoreDefinition definition) { Gson gson = new Gson(); Map map = gson.fromJson(string, new TypeToken>() { - - /** - * - */ private static final long serialVersionUID = 1L; }.getType()); DataStore dataStore = DataStore.newInstance(definition); for (Entry entry : map.entrySet()) { - - // Determine key DataKey key = definition.getKey(entry.getKey()); - - // Decode value Object value = key.decode(entry.getValue()); - dataStore.setRaw(key, value); } return dataStore; } - // public static DataKey dataStoreProvider(String id, Class aClass, - // StoreDefinition definition) { - // - // return object(id, aClass) - // .setStoreDefinition(definition); - // } - + /** + * Creates a {@link DataKey} for an enumeration. + * + * @param id the identifier for the key + * @param anEnum the enumeration class + * @return a {@link DataKey} for enumeration values + */ public static > DataKey enumeration(String id, Class anEnum) { return object(id, anEnum) .setDefault(() -> anEnum.getEnumConstants()[0]) @@ -453,31 +528,49 @@ public static > DataKey enumeration(String id, Class anE .setKeyPanelProvider((key, data) -> new EnumMultipleChoicePanel<>(key, data)); } + /** + * Creates a {@link DataKey} for a list of enumeration values. + * + * @param id the identifier for the key + * @param anEnum the enumeration class + * @return a {@link DataKey} for a list of enumeration values + */ public static > DataKey> enumerationMulti(String id, Class anEnum) { return enumerationMulti(id, anEnum.getEnumConstants()); } + /** + * Creates a {@link DataKey} for a list of enumeration values. + * + * @param id the identifier for the key + * @param enums the enumeration values + * @return a {@link DataKey} for a list of enumeration values + */ @SuppressWarnings("unchecked") public static > DataKey> enumerationMulti(String id, T... enums) { - SpecsCheck.checkArgument(enums.length > 0, () -> "Must give at least one enum"); return multiplechoiceList(id, new EnumCodec<>((Class) enums[0].getClass()), Arrays.asList(enums)); } /** - * A DataKey that supports types with generics. - * - * - * @param id - * @param aClass - * @param defaultValue - * @return + * Creates a generic {@link DataKey} with a specified default value. + * + * @param id the identifier for the key + * @param exampleInstance an example instance of the key's value + * @return a {@link DataKey} for the specified type */ public static DataKey generic(String id, E exampleInstance) { return new GenericKey<>(id, exampleInstance); } + /** + * Creates a generic {@link DataKey} with a default value supplier. + * + * @param id the identifier for the key + * @param defaultSupplier the supplier for the default value + * @return a {@link DataKey} for the specified type + */ public static DataKey generic(String id, Supplier defaultSupplier) { DataKey datakey = new GenericKey<>(id, defaultSupplier.get()); datakey.setDefault(defaultSupplier); @@ -485,43 +578,28 @@ public static DataKey generic(String id, Supplier default } /** - * * - *

- * Can only store instances that have the same concrete type as the given example instance. You should only creating - * generic keys of concrete base types (e.g., ArrayList) and avoid interfaces or abstract classes (e.g., - * List). - * - * @param id - * @param aClass - * @return + * Creates a {@link DataKey} for a list of values. + * + * @param id the identifier for the key + * @param elementClass the class of the list's elements + * @return a {@link DataKey} for a list of values */ - // public static DataKey generic(String id, Class aClass) { - // return new NormalKey<>(id, aClass); - // } - - // - // public static > DataKey> enumList(String id, Class enumClass) { - // EnumList enumList = new EnumList<>(enumClass); - // return new NormalKey>(id, enumList.getClass()); - // } - - // public static DataKey> stringList2() { - // List list = Arrays.asList("asd", "asdas"); - // GenericKey> key = new GenericKey<>("", list); - // - // return key; - // } - @SuppressWarnings("unchecked") public static DataKey> list(String id, Class elementClass) { return generic(id, () -> (List) new ArrayList<>()) .setCustomSetter((value, data) -> KeyFactory.listCustomSetter(value, data, elementClass)) .setCopyFunction(ArrayList::new) .setValueClass(List.class); - // .setDefault(() -> new ArrayList<>()); - } + /** + * Custom setter for lists, ensuring the correct element type. + * + * @param value the list to set + * @param data the data store containing additional information + * @param elementClass the class of the list's elements + * @return the processed list + */ private static List listCustomSetter(List value, DataStore data, Class elementClass) { if (value instanceof ArrayList) { return SpecsCollections.cast(value, elementClass).toArrayList(); @@ -536,10 +614,10 @@ private static List listCustomSetter(List value, DataStore data, Class } /** - * Represents a set of files, with a corresponding base folder. - * - * @param id - * @return + * Creates a {@link DataKey} for a map of files with corresponding base folders. + * + * @param id the identifier for the key + * @return a {@link DataKey} for a map of files with base folders */ public static DataKey> filesWithBaseFolders(String id) { return generic(id, (Map) new HashMap()) @@ -548,14 +626,15 @@ public static DataKey> filesWithBaseFolders(String id) { .setCustomGetter(KeyFactory::customGetterFilesWithBaseFolders) .setCustomSetter(KeyFactory::customSetterFilesWithBaseFolders) .setDefault(() -> new HashMap()); - - // return new NormalKey<>(id, String.class) - // .setKeyPanelProvider((key, data) -> new StringPanel(key, data)) - // .setDecoder(s -> s) - // .setDefault(() -> ""); - } + /** + * Custom getter for files with base folders, processing paths and base folders. + * + * @param value the map of files with base folders + * @param data the data store containing additional information + * @return the processed map + */ public static Map customGetterFilesWithBaseFolders(Map value, DataStore data) { Map processedMap = new HashMap<>(); @@ -565,44 +644,35 @@ public static Map customGetterFilesWithBaseFolders(Map v File newBase = noBaseFolder ? null : customGetterFile(entry.getValue(), data, true, false, false, true); - // File oldBase = entry.getValue() == null ? new File(".") : entry.getValue(); - // File newBase = customGetterFile(oldBase, data, true, false, false, true); - processedMap.put(newPath, newBase); - - // System.out.println("PATH BEFORE:" + entry.getKey()); - // System.out.println("PATH AFTER:" + newPath); - // System.out.println("BASE BEFORE:" + entry.getValue()); - // System.out.println("BASE AFTER:" + newBase); } return processedMap; } + /** + * Custom setter for files with base folders, ensuring relative paths. + * + * @param value the map of files with base folders + * @param data the data store containing additional information + * @return the processed map + */ public static Map customSetterFilesWithBaseFolders(Map value, DataStore data) { - - // If it has no working folder set, just return value - // Optional workingFolderTry = data.getTry(JOptionKeys.CURRENT_FOLDER_PATH); Optional workingFolderTry = data.get(JOptionKeys.CURRENT_FOLDER_PATH); if (!workingFolderTry.isPresent()) { - // System.out.println("NO CURRENT FOLDER PATH"); return value; } File workingFolder = new File(workingFolderTry.get()); - Map processedMap = new HashMap<>(); - // Replace values with relative paths to the working folder, if there is a common base for (var entry : value.entrySet()) { - File previousPath = entry.getKey(); File previousBase = entry.getValue(); String newBase = entry.getValue() == null ? "" : SpecsIo.getRelativePath(previousBase, workingFolder, true).orElse(previousBase.toString()); - // New path must take into account base String newPath = SpecsIo.getRelativePath(previousPath, workingFolder, true) .orElse(previousPath.toString()); @@ -612,16 +682,14 @@ public static Map customSetterFilesWithBaseFolders(Map v return processedMap; } - // @SuppressWarnings("unchecked") - // public static > DataKey> multiplechoiceList(String id, T... enums) { - // - // } - - // public static DataKey> multiplechoiceList(String id, - // @SuppressWarnings("unchecked") T... availableChoices) { - // return multiplechoiceList(id, Arrays.asList(availableChoices)); - // } - + /** + * Creates a {@link DataKey} for a list of multiple-choice values. + * + * @param id the identifier for the key + * @param codec the codec for encoding and decoding values + * @param availableChoices the available choices for the key + * @return a {@link DataKey} for a list of multiple-choice values + */ public static DataKey> multiplechoiceList(String id, StringCodec codec, List availableChoices) { SpecsCheck.checkArgument(availableChoices.size() > 0, () -> "Must give at least one element"); @@ -632,12 +700,25 @@ public static DataKey> multiplechoiceList(String id, StringCodec (key, data) -> new MultipleChoiceListPanel<>(key, data)); } + /** + * Creates a {@link DataKey} for a list of multiple-choice string values. + * + * @param id the identifier for the key + * @param availableChoices the available choices for the key + * @return a {@link DataKey} for a list of multiple-choice string values + */ public static DataKey> multipleStringList(String id, String... availableChoices) { return multipleStringList(id, Arrays.asList(availableChoices)); } + /** + * Creates a {@link DataKey} for a list of multiple-choice string values. + * + * @param id the identifier for the key + * @param availableChoices the available choices for the key + * @return a {@link DataKey} for a list of multiple-choice string values + */ public static DataKey> multipleStringList(String id, List availableChoices) { return multiplechoiceList(id, s -> s, availableChoices); } - } diff --git a/jOptions/src/org/suikasoft/jOptions/Datakey/KeyUser.java b/jOptions/src/org/suikasoft/jOptions/Datakey/KeyUser.java index 93cdb2cd..250d5109 100644 --- a/jOptions/src/org/suikasoft/jOptions/Datakey/KeyUser.java +++ b/jOptions/src/org/suikasoft/jOptions/Datakey/KeyUser.java @@ -1,14 +1,14 @@ /** * Copyright 2015 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Datakey; @@ -20,63 +20,66 @@ import org.suikasoft.jOptions.storedefinition.StoreDefinition; /** - * Represents a class that uses DataKeys (for reading and/or setting values). - * - * @author JoaoBispo + * Interface for classes that use {@link DataKey} instances for reading and/or setting values. * + *

This interface provides methods to retrieve the keys that are read or written by the implementing class, and to validate a {@link DataStore} against the keys required by the class. */ public interface KeyUser { /** - * - * @return a list of keys needed by the implementing class + * Retrieves a collection of keys that are read by the implementing class. + * + *

This method returns an empty collection by default, indicating that the class does not read any keys. + * + * @return a collection of {@link DataKey} instances that are read by the class */ default Collection> getReadKeys() { - return Collections.emptyList(); + return Collections.emptyList(); } /** - * - * @return a list of keys that will be written by the implementing class + * Retrieves a collection of keys that are written by the implementing class. + * + *

This method returns an empty collection by default, indicating that the class does not write any keys. + * + * @return a collection of {@link DataKey} instances that are written by the class */ default Collection> getWriteKeys() { - return Collections.emptyList(); + return Collections.emptyList(); } /** - * Checks if the given DataStore contains values for all the keys used by the implementing class. If there is no - * value for any given key, an exception is thrown. - * - *

- * This method can only be called for DataStores that have a {@link StoreDefinition}. If no definition is present, - * throws an exception. - *

- * If 'noDefaults' is true, it forces a value to exist in the table, even if the key has a default value. - * - * @param data - * @param noDefaults + * Validates that the given {@link DataStore} contains values for all the keys required by the implementing class. + * + *

If any required key is missing, an exception is thrown. This method requires the {@link DataStore} to have a {@link StoreDefinition}. If no definition is present, an exception is thrown. + * + *

If the {@code noDefaults} parameter is set to {@code true}, the validation will require explicit values for all keys, even if the keys have default values. + * + * @param data the {@link DataStore} to validate + * @param noDefaults whether to enforce explicit values for all keys, ignoring default values + * @throws RuntimeException if the {@link DataStore} does not contain values for all required keys */ default void check(DataStore data, boolean noDefaults) { - if (!data.getStoreDefinitionTry().isPresent()) { - throw new RuntimeException("This method requires that the DataStore has a StoreDefinition"); - } + if (!data.getStoreDefinitionTry().isPresent()) { + throw new RuntimeException("This method requires that the DataStore has a StoreDefinition"); + } - for (DataKey key : data.getStoreDefinitionTry().get().getKeys()) { - // Check if the key is present - if (data.hasValue(key)) { - continue; - } + for (DataKey key : data.getStoreDefinitionTry().get().getKeys()) { + // Check if the key is present + if (data.hasValue(key)) { + continue; + } - // If defaults allowed, check if key has a default - if (!noDefaults && key.hasDefaultValue()) { - continue; - } + // If defaults allowed, check if key has a default + if (!noDefaults && key.hasDefaultValue()) { + continue; + } - final String defaultStatus = noDefaults ? "disabled" : "enabled"; + final String defaultStatus = noDefaults ? "disabled" : "enabled"; - // Check failed, throw exception - throw new RuntimeException("DataStore check failed, class needs a definition for '" + key.getName() - + "' that is not present in the StoreDefinition (defaults are " + defaultStatus + ")"); - } + // Check failed, throw exception + throw new RuntimeException("DataStore check failed, class needs a definition for '" + key.getName() + + "' that is not present in the StoreDefinition (defaults are " + defaultStatus + ")"); + } } } diff --git a/jOptions/src/org/suikasoft/jOptions/Datakey/MagicKey.java b/jOptions/src/org/suikasoft/jOptions/Datakey/MagicKey.java index 9bd278b5..f6ba412f 100644 --- a/jOptions/src/org/suikasoft/jOptions/Datakey/MagicKey.java +++ b/jOptions/src/org/suikasoft/jOptions/Datakey/MagicKey.java @@ -1,14 +1,14 @@ /** * Copyright 2014 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Datakey; @@ -22,49 +22,89 @@ import pt.up.fe.specs.util.SpecsStrings; import pt.up.fe.specs.util.parsing.StringCodec; +/** + * Special DataKey implementation for advanced or dynamic key scenarios. + * + *

This class is intended for use cases where the key type or behavior is determined dynamically or requires special handling. + * + * @param the type of value associated with this key + */ class MagicKey extends ADataKey { + /** + * Constructs a MagicKey with the given id. + * + * @param id the key id + */ + public MagicKey(String id) { + this(id, null, null); + } + + /** + * Constructs a MagicKey with the given id, default value, and decoder. + * + * @param id the key id + * @param defaultValue the default value provider + * @param decoder the string decoder + */ private MagicKey(String id, Supplier defaultValue, StringCodec decoder) { this(id, defaultValue, decoder, null, null, null, null, null, null, null); } + /** + * Full constructor for MagicKey with all options. + * + * @param id the key id + * @param defaultValueProvider the default value provider + * @param decoder the string decoder + * @param customGetter the custom getter + * @param panelProvider the panel provider + * @param label the label + * @param definition the store definition + * @param copyFunction the copy function + * @param customSetter the custom setter + * @param extraData extra data for the key + */ private MagicKey(String id, Supplier defaultValueProvider, StringCodec decoder, CustomGetter customGetter, KeyPanelProvider panelProvider, String label, StoreDefinition definition, Function copyFunction, CustomGetter customSetter, DataKeyExtraData extraData) { - super(id, defaultValueProvider, decoder, customGetter, panelProvider, label, definition, copyFunction, customSetter, extraData); } - public MagicKey(String id) { - this(id, null, null); - // this.id = id; - // this.decoder = null; - } - - /* - public static MagicKey create(String id) { - return new MagicKey(id) { - }; - } - */ - + /** + * Returns the value class for this key, inferred from the generic type parameter. + * + * @return the value class + */ @SuppressWarnings("unchecked") @Override public Class getValueClass() { return (Class) SpecsStrings.getSuperclassTypeParameter(this.getClass()); } + /** + * Creates a copy of this MagicKey with the given parameters. + * + * @param id the key id + * @param defaultValueProvider the default value provider + * @param decoder the string decoder + * @param customGetter the custom getter + * @param panelProvider the panel provider + * @param label the label + * @param definition the store definition + * @param copyFunction the copy function + * @param customSetter the custom setter + * @param extraData extra data for the key + * @return a new MagicKey instance + */ @SuppressWarnings({ "rawtypes", "unchecked" }) @Override protected DataKey copy(String id, Supplier defaultValueProvider, StringCodec decoder, CustomGetter customGetter, KeyPanelProvider panelProvider, String label, StoreDefinition definition, Function copyFunction, CustomGetter customSetter, DataKeyExtraData extraData) { - return new MagicKey(id, defaultValueProvider, decoder, customGetter, panelProvider, label, definition, copyFunction, customSetter, extraData) { - }; } - } diff --git a/jOptions/src/org/suikasoft/jOptions/Datakey/NormalKey.java b/jOptions/src/org/suikasoft/jOptions/Datakey/NormalKey.java index 0ffa5c6f..67a14ca4 100644 --- a/jOptions/src/org/suikasoft/jOptions/Datakey/NormalKey.java +++ b/jOptions/src/org/suikasoft/jOptions/Datakey/NormalKey.java @@ -8,7 +8,7 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Datakey; @@ -22,53 +22,87 @@ import pt.up.fe.specs.util.parsing.StringCodec; /** - * Simple implementation of DataKey. + * Simple implementation of {@link DataKey} for non-generic types. * - * TODO: Make class extend ADataKey instead of DataKey. - * - * @author JoaoBispo - * - * @param + * @param the type of value associated with this key */ public class NormalKey extends ADataKey { - // TODO: Temporarily removing 'transient' from aClass and default value, while phasing out Setup private final Class aClass; + /** + * Constructs a NormalKey with the given id and value class. + * + * @param id the key id + * @param aClass the value class + */ public NormalKey(String id, Class aClass) { this(id, aClass, () -> null); } + /** + * Constructs a NormalKey with the given id, value class, and default value provider. + * + * @param id the key id + * @param aClass the value class + * @param defaultValue the default value provider + */ + public NormalKey(String id, Class aClass, Supplier defaultValue) { + this(id, aClass, defaultValue, null, null, null, null, null, null, null, null); + } + + /** + * Full constructor for NormalKey with all options. + * + * @param id the key id + * @param aClass the value class + * @param defaultValueProvider the default value provider + * @param decoder the string decoder for the value + * @param customGetter the custom getter for the value + * @param panelProvider the panel provider for the key + * @param label the label for the key + * @param definition the store definition + * @param copyFunction the function to copy the value + * @param customSetter the custom setter for the value + * @param extraData additional data for the key + */ protected NormalKey(String id, Class aClass, Supplier defaultValueProvider, StringCodec decoder, CustomGetter customGetter, KeyPanelProvider panelProvider, String label, StoreDefinition definition, Function copyFunction, CustomGetter customSetter, DataKeyExtraData extraData) { - super(id, defaultValueProvider, decoder, customGetter, panelProvider, label, definition, copyFunction, customSetter, extraData); - this.aClass = aClass; } /** + * Creates a copy of this NormalKey with the specified parameters. * - * @param id - * @param aClass - * @param defaultValue + * @param id the key id + * @param defaultValueProvider the default value provider + * @param decoder the string decoder for the value + * @param customGetter the custom getter for the value + * @param panelProvider the panel provider for the key + * @param label the label for the key + * @param definition the store definition + * @param copyFunction the function to copy the value + * @param customSetter the custom setter for the value + * @param extraData additional data for the key + * @return a new NormalKey instance */ - public NormalKey(String id, Class aClass, Supplier defaultValue) { - this(id, aClass, defaultValue, null, null, null, null, null, null, null, null); - } - @Override protected DataKey copy(String id, Supplier defaultValueProvider, StringCodec decoder, CustomGetter customGetter, KeyPanelProvider panelProvider, String label, StoreDefinition definition, Function copyFunction, CustomGetter customSetter, DataKeyExtraData extraData) { - return new NormalKey<>(id, aClass, defaultValueProvider, decoder, customGetter, panelProvider, label, definition, copyFunction, customSetter, extraData); } + /** + * Gets the class of the value associated with this key. + * + * @return the value class + */ @Override public Class getValueClass() { return aClass; diff --git a/jOptions/src/org/suikasoft/jOptions/Datakey/customkeys/MultipleChoiceListKey.java b/jOptions/src/org/suikasoft/jOptions/Datakey/customkeys/MultipleChoiceListKey.java index 3ca4e9c1..75ddf0cb 100644 --- a/jOptions/src/org/suikasoft/jOptions/Datakey/customkeys/MultipleChoiceListKey.java +++ b/jOptions/src/org/suikasoft/jOptions/Datakey/customkeys/MultipleChoiceListKey.java @@ -1,14 +1,14 @@ /** * Copyright 2019 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Datakey.customkeys; @@ -23,32 +23,27 @@ /** * Represents a key with several choices from which several can be selected. - * - * @author JoaoBispo * - * @param + * @param the type of the available choices */ public class MultipleChoiceListKey extends GenericKey> { + /** + * DataKey for the available choices for this key. + */ @SuppressWarnings("rawtypes") public static final DataKey AVAILABLE_CHOICES = KeyFactory.object("availableChoices", List.class); - // private final List availableChoices; - // private final Class elementClass; - + /** + * Constructs a MultipleChoiceListKey with the given id and available choices. + * + * @param id the key id + * @param availableChoices the list of available choices + */ @SuppressWarnings("unchecked") public MultipleChoiceListKey(String id, List availableChoices) { - // Always have extra data super(id, new ArrayList<>(availableChoices), null, null, null, null, null, null, null, null, new DataKeyExtraData()); getExtraData().get().set(AVAILABLE_CHOICES, availableChoices); - // this.availableChoices = availableChoices; - // elementClass = (Class) availableChoices.get(0).getClass(); } - - // public List getAvailableChoices() { - // // return availableChoices; - // return SpecsCollections.cast(getExtraData().get().get(AVAILABLE_CHOICES), elementClass); - // } - } diff --git a/jOptions/src/org/suikasoft/jOptions/GenericImplementations/DummyPersistence.java b/jOptions/src/org/suikasoft/jOptions/GenericImplementations/DummyPersistence.java index 03455ad2..5fd7d222 100644 --- a/jOptions/src/org/suikasoft/jOptions/GenericImplementations/DummyPersistence.java +++ b/jOptions/src/org/suikasoft/jOptions/GenericImplementations/DummyPersistence.java @@ -1,11 +1,11 @@ -/** +/* * Copyright 2013 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -20,42 +20,56 @@ import org.suikasoft.jOptions.storedefinition.StoreDefinition; /** - * Dummy implementation of persistence, just to test code. - * + * Dummy implementation of AppPersistence for testing purposes. *

- * Setup is maintained in memory, instead of being saved to a file. - * + * This implementation keeps the DataStore in memory and does not persist to disk. + * * @author Joao Bispo - * */ public class DummyPersistence implements AppPersistence { private DataStore setup; + /** + * Constructs a DummyPersistence with the given DataStore. + * + * @param setup the DataStore to use + */ public DummyPersistence(DataStore setup) { - this.setup = setup; - + this.setup = setup; } + /** + * Constructs a DummyPersistence with a new DataStore from the given StoreDefinition. + * + * @param setupDefinition the StoreDefinition to use + */ public DummyPersistence(StoreDefinition setupDefinition) { - this(DataStore.newInstance(setupDefinition)); + this(DataStore.newInstance(setupDefinition)); } - /* (non-Javadoc) - * @see org.suikasoft.jOptions.Interfaces.AppPersistence#loadData(java.io.File) + /** + * Loads the DataStore. Ignores the file and returns the in-memory DataStore. + * + * @param file the file to load (ignored) + * @return the in-memory DataStore */ @Override public DataStore loadData(File file) { - return setup; + return setup; } - /* (non-Javadoc) - * @see org.suikasoft.jOptions.Interfaces.AppPersistence#saveData(java.io.File, org.suikasoft.jOptions.Interfaces.Setup, boolean) + /** + * Saves the DataStore. Ignores the file and keeps the DataStore in memory. + * + * @param file the file to save (ignored) + * @param setup the DataStore to save + * @param keepSetupFile whether to keep the setup file (ignored) + * @return true always */ @Override public boolean saveData(File file, DataStore setup, boolean keepSetupFile) { - this.setup = setup; - return true; + this.setup = setup; + return true; } - } diff --git a/jOptions/src/org/suikasoft/jOptions/Interfaces/AliasProvider.java b/jOptions/src/org/suikasoft/jOptions/Interfaces/AliasProvider.java index c1359502..1caa5cdb 100644 --- a/jOptions/src/org/suikasoft/jOptions/Interfaces/AliasProvider.java +++ b/jOptions/src/org/suikasoft/jOptions/Interfaces/AliasProvider.java @@ -1,14 +1,14 @@ /** * Copyright 2013 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Interfaces; @@ -16,15 +16,22 @@ import java.util.Map; /** - * @author Joao Bispo - * + * Interface for providing alias mappings for names. + *

+ * This interface defines a contract for classes that need to provide mappings between alias names and their + * corresponding original names. Implementing classes should ensure that the mappings are accurate and up-to-date. + *

*/ public interface AliasProvider { /** * Maps alias names to the corresponding original name. - * - * @return + *

+ * This method returns a map where the keys are alias names and the values are the original names they represent. + * The map should not contain null keys or values. + *

+ * + * @return a map from alias to original name */ Map getAlias(); } diff --git a/jOptions/src/org/suikasoft/jOptions/Interfaces/DataStore.java b/jOptions/src/org/suikasoft/jOptions/Interfaces/DataStore.java index e163cc41..f6f722c0 100644 --- a/jOptions/src/org/suikasoft/jOptions/Interfaces/DataStore.java +++ b/jOptions/src/org/suikasoft/jOptions/Interfaces/DataStore.java @@ -1,14 +1,14 @@ /** * Copyright 2015 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Interfaces; @@ -39,17 +39,28 @@ /** * A key-value store for arbitrary objects, with type-safe keys. - * - * @author JoaoBispo - * @see SimpleDataStore * + *

Implements {@link DataClass} for DataStore-specific operations. */ public interface DataStore extends DataClass { - // Optional set(DataKey key, E value); + /** + * Sets the value for the given key. + * + * @param key the key + * @param value the value to set + * @return this DataStore + */ @Override DataStore set(DataKey key, E value); + /** + * Helper method for setting a value, returns this DataStore. + * + * @param key the key + * @param value the value to set + * @return this DataStore + */ default DataStore put(DataKey key, E value) { set(key, value); return this; @@ -57,232 +68,176 @@ default DataStore put(DataKey key, E value) { /** * Only sets the value if there is not a value yet for the given key. - * - * @param key - * @param value + * + * @param key the key + * @param value the value to set if not present */ default void setIfNotPresent(DataKey key, E value) { if (hasValue(key)) { return; } - set(key, value); } /** - * Helper method when we do not have the key, only its id. - * - * @param key - * @param value - * @return + * Sets a value for a key by its string id. + * + * @param key the key id + * @param value the value + * @return an Optional containing the previous value, if any */ Optional setRaw(String key, Object value); /** - * Helper method for when the type of the DataKey is unknown (e.g., when working with DataKeys in bulk). - * - * @param key - * @param value - * @return + * Sets a value for a DataKey when the type is unknown. + * + * @param key the DataKey + * @param value the value + * @return this DataStore */ - // Check is being done manually @SuppressWarnings("unchecked") - // default Optional setRaw(DataKey key, Object value) { default DataStore setRaw(DataKey key, Object value) { - // Do not allow the storage of other DataStores - // if (value instanceof DataStore) { - // LoggingUtils.msgWarn("Key '" + key.getName() - // + "' uses DataStore as its type. Storing directly DataStore objects is discouraged"); - // // throw new RuntimeException("Storing other DataStore objects directly is not allowed"); - // } - - // Check value is instance compatible with key if (!key.getValueClass().isInstance(value)) { - throw new RuntimeException("Value '" + value + "' of type '" + value.getClass() - + "' is not compatible with key '" + key + "'"); + throw new IllegalArgumentException("Value is not of the correct type for key '" + key.getName() + "'"); } - - return set((DataKey) key, value); + set((DataKey) key, value); + return this; } /** - * Uses the decoder in the key to decode the string. In no decoder is found, throws an exception. - * - * @param key - * @param value - * @return + * Sets a value for a DataKey using its decoder to decode a string. + * + * @param key the DataKey + * @param value the string value to decode and set + * @return this DataStore */ - // default Optional setString(DataKey key, String value) { default DataStore setString(DataKey key, String value) { if (!key.getDecoder().isPresent()) { throw new RuntimeException("No decoder set for key '" + key + "'"); } - return set(key, key.getDecoder().get().decode(value)); } /** - * Adds the values of the given setup. - *

- * TODO: Change to DataStore - * - * @param dataStore + * Adds the values of the given DataStore to this DataStore. + * + * @param dataStore the DataStore to add values from + * @return this DataStore */ - // @Override - // DataStore set(DataStore dataStore); @Override default DataStore set(DataStore dataStore) { - StoreDefinition definition = getStoreDefinitionTry().orElse(null); - // for (DataKey key : dataStore.keysWithValues()) { for (String key : dataStore.getKeysWithValues()) { - - // Check if key exists if (definition != null && isClosed() && !definition.hasKey(key)) { SpecsLogs.debug( "set(DataStore): value with key '" + key + "' not part of this definition: " + definition); continue; } - setRaw(key, dataStore.get(key)); } - return this; } /** - * Configures the current DataStore to behave strictly, i.e., can only access values with keys that have been added - * before. - * - *

- * For instance, if a get() is performed for a key that was not used before, or if the value is null, instead of - * returning a default value, throws an exception. - * - * @param value + * Configures the current DataStore to behave strictly. + * + *

Strict mode means that only keys that have been added before can be accessed. + * + * @param value true to enable strict mode, false to disable */ void setStrict(boolean value); /** * Sets a StoreDefinition for this DataStore. - * - *

- * Can only set a StoreDefinition once, subsequent calls to this function will throw an exception. - * - * @param storeDefinition + * + *

Can only set a StoreDefinition once, subsequent calls to this function will throw an exception. + * + * @param definition the StoreDefinition to set */ - // void setStoreDefinition(StoreDefinition storeDefinition); + void setStoreDefinition(StoreDefinition definition); /** - * - * @return an Optional containing a StoreDefinition, if defined + * Sets a StoreDefinition for this DataStore using a class. + * + * @param aClass the class to derive the StoreDefinition from */ - // Optional getStoreDefinition(); - - void setStoreDefinition(StoreDefinition definition); - default void setDefinition(Class aClass) { setStoreDefinition(StoreDefinitions.fromInterface(aClass)); } /** - * Adds a new key and value. - * - *

- * Throws an exception if there already is a value for the given key. - * - * @param key + * Adds a new key and value to the DataStore. + * + *

Throws an exception if there already is a value for the given key. + * + * @param key the key + * @param value the value to add + * @return this DataStore */ default DataStore add(DataKey key, E value) { Preconditions.checkArgument(key != null); SpecsCheck.checkArgument(!hasValue(key), () -> "Attempting to add value already in PassData: " + key); - set(key, value); - return this; } + /** + * Adds all values from a DataView to this DataStore. + * + * @param values the DataView to add values from + * @return this DataStore + */ default DataStore addAll(DataView values) { - - // for (DataKey key : values.keysWithValues()) { for (String key : values.getKeysWithValues()) { setRaw(key, values.getValueRaw(key)); } - - // Map rawValues = values.getValuesMap(); - // - // for (String key : rawValues.keySet()) { - // DataKey datakey = KeyFactory.object(key, Object.class); - // set(datakey, rawValues.get(key)); - // } - return this; - // for (DataKey key : values.getKeys()) { - // add((DataKey) key, values.getValue(key)); - // } } /** - * Sets all values of the given DataStore in the given DataStore. - * - * @param values - * @return + * Adds all values from another DataStore to this DataStore. + * + * @param values the DataStore to add values from + * @return this DataStore */ default DataStore addAll(DataStore values) { addAll(DataView.newInstance(values)); - return this; } + /** + * Adds all values from a DataClass to this DataStore. + * + * @param values the DataClass to add values from + * @return this DataStore + */ default DataStore addAll(DataClass values) { - for (DataKey key : values.getDataKeysWithValues()) { Object value = values.get(key); setRaw(key, value); } - return this; } /** * Replaces the value of an existing key. - * - *

- * Throws an exception if there is no value for the given key. - * - * @param key + * + *

Throws an exception if there is no value for the given key. + * + * @param key the key + * @param value the new value to set */ default void replace(DataKey key, E value) { Preconditions.checkArgument(key != null); Preconditions.checkArgument(hasValue(key), "Attempting to replace value for key not yet in PassData: " + key); - set(key, value); } - // default StoreDefinition getDefinition() { - // return StoreDefinition.newInstance(getName(), getKeys()); - // } - - /** - * Convenience method for interfacing with DataViews. - * - * @param setup - */ - /* - default void setValues(DataView setup) { - if (setup instanceof DataStoreContainer) { - setValues(((DataStoreContainer) setup).getDataStore()); - return; - } - - setValues(new SimpleDataStore("temp_setup", setup)); - } - */ - /** - * The name of the setup. - * - * @return + * Returns the name of the DataStore. + * + * @return the name of the DataStore */ String getName(); @@ -292,143 +247,80 @@ default String getDataClassName() { } /** - * Returns the value associated with the given key. If there is no value for the key, returns the default value - * defined by the key. - * - *

- * This method always returns a value, and throws an exception if it can't. - * - * - * This method is safe, when using DataKey, it can only store objects of the key type. - * - * @param key - * @return + * Returns the value associated with the given key. + * + *

If there is no value for the key, returns the default value defined by the key. + * + * @param key the key + * @return the value associated with the key */ @Override T get(DataKey key); /** * Removes a value from the DataStore. - * - * @param key - * @return + * + * @param key the key + * @return an Optional containing the removed value, if any */ Optional remove(DataKey key); /** - * - * @param key - * @return true, if it contains a non-null value for the given key, not considering default values + * Checks if the DataStore contains a non-null value for the given key. + * + * @param key the key + * @return true if the DataStore contains a non-null value for the key, false otherwise */ @Override boolean hasValue(DataKey key); - // default boolean hasValueRaw(String key) { - // return getValuesMap().get(key) != null; - // } - /** * Tries to return a value from the DataStore. - * - *

- * Does not use default values. If the key is not in the map, or there is no value mapped to the given key, returns - * an empty Optional. - * - * @param key - * @return + * + *

Does not use default values. If the key is not in the map, or there is no value mapped to the given key, + * returns an empty Optional. + * + * @param key the key + * @return an Optional containing the value, if present */ default Optional getTry(DataKey key) { if (!hasValue(key)) { return Optional.empty(); } - return Optional.of(get(key)); } - // Map getValuesMap(); - - default Collection getValues() { - List values = new ArrayList<>(); - - for (String key : getKeysWithValues()) { - values.add(get(key)); - } - - return values; - } - /** - * - * @param id - * @return the value mapped to the given key id, or null if no value is mapped + * Returns the value mapped to the given key id, or null if no value is mapped. + * + * @param id the key id + * @return the value mapped to the key id, or null if no value is mapped */ Object get(String id); /** - * Helper method for when the type of the DataKey is unknown (e.g., when working with DataKeys in bulk). - * - * @param key - * @param value - * @return - */ - // Check is being done manually - @SuppressWarnings("unchecked") - default Object getRaw(DataKey key) { - - Object value = get((DataKey) key); - - // Check value is instance compatible with key - if (!key.getValueClass().isInstance(value)) { - throw new RuntimeException("Value '" + value + "' of type '" + value.getClass() - + "' is not compatible with key '" + key + "'"); - } - - return value; - } - - // default Object get(String id) { - // return getValuesMap().get(id); - // } - - /** - * - * - * - * @return All the keys in the DataStore that are mapped to a value + * Returns all the keys in the DataStore that are mapped to a value. + * + * @return a collection of keys with values */ Collection getKeysWithValues(); /** - * If DataStore has a StoreDefinition, uses the copy function defined in the DataKeys. - * - *

- * Otherwise, throws exception. - * - * @return + * Creates a copy of this DataStore. + * + *

If the DataStore has a StoreDefinition, uses the copy function defined in the DataKeys. + * + * @return a copy of this DataStore */ default DataStore copy() { - // Otherwise, tries to use the - // object copy constructor. if (!getStoreDefinitionTry().isPresent()) { throw new RuntimeException("No StoreDefinition defined, cannot copy. DataStore: " + this); - // DataStore copy = DataStore.newInstance(getName()); - // - // for (String key : getKeysWithValues()) { - // copy.setRaw(key, SpecsSystem.copy(get(key))); - // } - // - // return copy; } StoreDefinition def = getStoreDefinitionTry().get(); - // .orElseThrow( - // () -> new RuntimeException("Can only copy DataStores that have defined a StoreDefinition")); - - // Create new DataStore with same StoreDefinition DataStore copy = DataStore.newInstance(def, isClosed()); for (DataKey key : def.getKeys()) { - // Skip keys without values if (!hasValue(key)) { continue; } @@ -440,29 +332,32 @@ default DataStore copy() { return copy; } - /* - * CONSTRUCTORS - */ /** - * - * @param name - * @return + * Creates a new DataStore instance with the given name. + * + * @param name the name of the DataStore + * @return a new DataStore instance */ public static DataStore newInstance(String name) { return new SimpleDataStore(name); } + /** + * Creates a new DataStore instance with the given StoreDefinition. + * + * @param storeDefinition the StoreDefinition + * @return a new DataStore instance + */ public static DataStore newInstance(StoreDefinition storeDefinition) { return newInstance(storeDefinition, false); } /** - * - * @param storeDefinition - * @param closed - * if true, no other keys besides the ones defined in the StoreDefinition can be added. This allows a - * more efficient implementation of DataStore to be used. - * @return + * Creates a new DataStore instance with the given StoreDefinition and closed state. + * + * @param storeDefinition the StoreDefinition + * @param closed if true, no other keys besides the ones defined in the StoreDefinition can be added + * @return a new DataStore instance */ public static DataStore newInstance(StoreDefinition storeDefinition, boolean closed) { if (closed) { @@ -470,30 +365,47 @@ public static DataStore newInstance(StoreDefinition storeDefinition, boolean clo } else { return new SimpleDataStore(storeDefinition); } - } + /** + * Creates a new DataStore instance from a StoreDefinitionProvider. + * + * @param storeProvider the StoreDefinitionProvider + * @return a new DataStore instance + */ public static DataStore newInstance(StoreDefinitionProvider storeProvider) { return newInstance(storeProvider.getStoreDefinition()); } + /** + * Creates a new DataStore instance from a DataView. + * + * @param dataView the DataView + * @return a new DataStore instance + */ public static DataStore newInstance(DataView dataView) { if (dataView instanceof DataStoreContainer) { return ((DataStoreContainer) dataView).getDataStore(); } - throw new RuntimeException("Not implemented yet."); } + /** + * Creates a new DataStore instance with the given name and values from another DataStore. + * + * @param name the name of the DataStore + * @param dataStore the DataStore to copy values from + * @return a new DataStore instance + */ public static DataStore newInstance(String name, DataStore dataStore) { return new SimpleDataStore(name, dataStore); } /** * Creates a DataStore from all the public static DataKeys that can be found in the class. - * - * @param aClass - * @return + * + * @param aClass the class to derive the DataStore from + * @return a new DataStore instance */ public static DataStore newInstance(Class aClass) { return newInstance(StoreDefinitions.fromInterface(aClass)); @@ -503,13 +415,6 @@ public static DataStore newInstance(Class aClass) { default String toInlinedString() { Collection keys = getKeysWithValues(); - // - // getStoreDefinition().map(def -> def.getKeys().stream() - // .filter(key -> hasValue(key)) - // .map(key -> key.getName()) - // .collect((Collection) Collectors.toList())) - // .orElse(getKeysWithValues()); - if (getStoreDefinitionTry().isPresent()) { keys = getStoreDefinitionTry().get().getKeys().stream() .filter(key -> hasValue(key)) @@ -517,36 +422,9 @@ default String toInlinedString() { .collect(Collectors.toList()); } - // return getKeysWithValues().stream() - /* - StringBuilder builder = new StringBuilder(); - builder.append(getName() + " ["); - boolean firstTime = true; - for (String key : keys) { - if (firstTime) { - firstTime = false; - } else { - builder.append(", "); - } - - Object object = get(key); - if (object instanceof DataClass) { - ((DataClass) object). - continue; - } - - String keyString = key + ":" + get(key); - builder.append(keyString); - } - builder.append("]"); - - return builder.toString(); - */ - return keys.stream() .map(key -> key + ": " + DataClassUtils.toString(get(key))) .collect(Collectors.joining(", ", getName() + " [", "]")); - } @Override @@ -554,8 +432,6 @@ default Collection> getDataKeysWithValues() { StoreDefinition storeDefinition = getStoreDefinitionTry().orElse(null); if (storeDefinition == null) { - // SpecsLogs.msgInfo( - // "keysWithValues: current DataStore does not have a StoreDefinition, returning empty list"); return Collections.emptyList(); } @@ -568,41 +444,40 @@ default Collection> getDataKeysWithValues() { } /** - * - * - * @return If this DataStore was loaded using AppPersistence, returns the instance that was used to load it + * Returns the AppPersistence instance that was used to load this DataStore, if any. + * + * @return an Optional containing the AppPersistence instance, if present */ default Optional getPersistence() { return Optional.empty(); } /** - * Sets the AppPersistence instance that was used to load this DataStore - * - * @param persistence - * @return + * Sets the AppPersistence instance that was used to load this DataStore. + * + * @param persistence the AppPersistence instance + * @return this DataStore */ default DataStore setPersistence(AppPersistence persistence) { - // Do nothing return this; } /** - * - * @return if this DataStore was loaded using a configuration file, returns the File that was used + * Returns the configuration file that was used to load this DataStore, if any. + * + * @return an Optional containing the configuration file, if present */ default Optional getConfigFile() { return Optional.empty(); } /** - * Sets the File that was used to load this DataStore - * - * @param configFile - * @return + * Sets the configuration file that was used to load this DataStore. + * + * @param configFile the configuration file + * @return this DataStore */ default DataStore setConfigFile(File configFile) { - // Do nothing return this; } } diff --git a/jOptions/src/org/suikasoft/jOptions/Interfaces/DataView.java b/jOptions/src/org/suikasoft/jOptions/Interfaces/DataView.java index 9d8f2719..d785c562 100644 --- a/jOptions/src/org/suikasoft/jOptions/Interfaces/DataView.java +++ b/jOptions/src/org/suikasoft/jOptions/Interfaces/DataView.java @@ -1,14 +1,14 @@ /** * Copyright 2015 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Interfaces; @@ -19,91 +19,102 @@ import org.suikasoft.jOptions.Datakey.DataKey; /** - * * A read-only view of a DataStore. - * - * @author JoaoBispo - * @see DefaultCleanSetup - * */ - public interface DataView { /** - * The name of the data store. - * - * @return + * Returns the name of the data store. + * + * @return the name */ String getName(); /** * Returns the value mapped to the given key. - * - * @param key - * @return + * + * @param key the key + * @return the value mapped to the key */ T getValue(DataKey key); + /** + * Returns the value mapped to the given key id. + * + * @param id the key id + * @return the value mapped to the key id + */ Object getValueRaw(String id); + /** + * Returns the DataKeys that have values in this view. + * + * @return a collection of DataKeys with values + */ Collection> getDataKeysWithValues(); + /** + * Returns the key ids that have values in this view. + * + * @return a collection of key ids with values + */ Collection getKeysWithValues(); + /** + * Returns the value mapped to the given DataKey, as an Object. + * + * @param key the DataKey + * @return the value mapped to the key + */ default Object getValueRaw(DataKey key) { return getValueRaw(key.getName()); } /** - * - * @param key - * @return true if the store contains a value for the given key + * Checks if the store contains a value for the given key. + * + * @param key the key + * @return true if the store contains a value for the key */ boolean hasValue(DataKey key); /** - * - * @return the objects mapped to the key ids + * Returns a new DataView instance backed by the given DataStore. + * + * @param dataStore the DataStore + * @return a new DataView instance */ - // Map getValuesMap(); - public static DataView newInstance(DataStore dataStore) { return new DefaultCleanSetup(dataStore); } + /** + * Returns an empty DataView instance. + * + * @return an empty DataView + */ public static DataView empty() { return new DataView() { - @Override public T getValue(DataKey key) { return null; } - @Override public String getName() { return ""; } - - // @Override - // public Map getValuesMap() { - // return Collections.emptyMap(); - // } - @Override public boolean hasValue(DataKey key) { return false; } - @Override public Object getValueRaw(String id) { return null; } - @Override public Collection> getDataKeysWithValues() { return Collections.emptyList(); } - @Override public Collection getKeysWithValues() { return Collections.emptyList(); @@ -111,6 +122,11 @@ public Collection getKeysWithValues() { }; } + /** + * Converts this DataView to a DataStore. + * + * @return a new DataStore + */ default DataStore toDataStore() { return DataStore.newInstance(this); } diff --git a/jOptions/src/org/suikasoft/jOptions/Interfaces/DefaultCleanSetup.java b/jOptions/src/org/suikasoft/jOptions/Interfaces/DefaultCleanSetup.java index 8648414f..85628eef 100644 --- a/jOptions/src/org/suikasoft/jOptions/Interfaces/DefaultCleanSetup.java +++ b/jOptions/src/org/suikasoft/jOptions/Interfaces/DefaultCleanSetup.java @@ -1,14 +1,14 @@ /** * Copyright 2015 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Interfaces; @@ -19,63 +19,101 @@ import org.suikasoft.jOptions.Datakey.DataKey; /** - * Default implementation of a {code CleanSetup}, backed-up by a {code CleanSetupBuilder}. - * - * @author JoaoBispo - * + * Default implementation of a {@code DataView}, backed by a {@code DataStore}. */ public class DefaultCleanSetup implements DataView, DataStoreContainer { private final DataStore data; + /** + * Creates a new DefaultCleanSetup backed by the given DataStore. + * + * @param data the DataStore to back this view + */ public DefaultCleanSetup(DataStore data) { this.data = data; } + /** + * Retrieves the name of the DataStore backing this view. + * + * @return the name of the DataStore + */ @Override public String getName() { return data.getName(); } + /** + * Retrieves the value associated with the given DataKey. + * + * @param key the DataKey whose value is to be retrieved + * @param the type of the value + * @return the value associated with the given DataKey + */ @Override public T getValue(DataKey key) { return data.get(key); } /** - * - * @return the DataStore backing the view + * Returns the DataStore backing this view. + * + * @return the DataStore */ @Override public DataStore getDataStore() { return data; } + /** + * Returns a string representation of the DataStore backing this view. + * + * @return a string representation of the DataStore + */ @Override public String toString() { return data.toString(); } - // @Override - // public Map getValuesMap() { - // return data.getValuesMap(); - // } - + /** + * Checks if the given DataKey has an associated value in the DataStore. + * + * @param key the DataKey to check + * @param the type of the value + * @return {@code true} if the DataKey has an associated value, {@code false} otherwise + */ @Override public boolean hasValue(DataKey key) { return data.hasValue(key); } + /** + * Retrieves the raw value associated with the given identifier. + * + * @param id the identifier whose value is to be retrieved + * @return the raw value associated with the given identifier + */ @Override public Object getValueRaw(String id) { return data.get(id); } + /** + * Retrieves all DataKeys that have associated values in the DataStore. + * + * @return a collection of DataKeys with associated values + */ @Override public Collection> getDataKeysWithValues() { return data.getDataKeysWithValues(); } + /** + * Retrieves all keys that have associated values in the DataStore. + * + * @return a collection of keys with associated values + */ @Override public Collection getKeysWithValues() { return data.getKeysWithValues(); diff --git a/jOptions/src/org/suikasoft/jOptions/JOptionKeys.java b/jOptions/src/org/suikasoft/jOptions/JOptionKeys.java index 03f2d325..6662fe9c 100644 --- a/jOptions/src/org/suikasoft/jOptions/JOptionKeys.java +++ b/jOptions/src/org/suikasoft/jOptions/JOptionKeys.java @@ -1,11 +1,11 @@ -/** - * Copyright 2016 SPeCS. - * +/* + * Copyright 2016 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -20,44 +20,48 @@ import org.suikasoft.jOptions.Datakey.KeyFactory; import org.suikasoft.jOptions.Interfaces.DataStore; +/** + * Common DataKeys and utility methods for jOptions context path management. + */ public interface JOptionKeys { + /** + * DataKey for the current folder path (optional String). + */ DataKey> CURRENT_FOLDER_PATH = KeyFactory.optional("joptions_current_folder_path"); + /** + * DataKey for using relative paths (Boolean). + */ DataKey USE_RELATIVE_PATHS = KeyFactory.bool("joptions_use_relative_paths"); /** * If the path is not absolute and CURRENT_FOLDER_PATH is set, returns a path relative to that set folder. - * - * @param currentFile - * @param dataStore - * @return + * + * @param currentFile the file whose context path is to be resolved + * @param dataStore the DataStore containing context information + * @return a File object representing the resolved path */ public static File getContextPath(File currentFile, DataStore dataStore) { Optional workingFolder = dataStore.get(JOptionKeys.CURRENT_FOLDER_PATH); - // No folder set, just return if (!workingFolder.isPresent()) { return currentFile; } - // Path is absolute, respect that if (currentFile.isAbsolute()) { return currentFile; - } - // Path is relative, create new file with set folder as parent File parentFolder = new File(workingFolder.get()); return new File(parentFolder, currentFile.getPath()); - } /** * Overload that accepts a String instead of a File. - * - * @param currentPath - * @param dataStore - * @return + * + * @param currentPath the path as a String + * @param dataStore the DataStore containing context information + * @return a File object representing the resolved path */ public static File getContextPath(String currentPath, DataStore dataStore) { return getContextPath(new File(currentPath), dataStore); diff --git a/jOptions/src/org/suikasoft/jOptions/JOptionsUtils.java b/jOptions/src/org/suikasoft/jOptions/JOptionsUtils.java index ac13f043..5a7d955d 100644 --- a/jOptions/src/org/suikasoft/jOptions/JOptionsUtils.java +++ b/jOptions/src/org/suikasoft/jOptions/JOptionsUtils.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions; @@ -30,19 +30,18 @@ import pt.up.fe.specs.util.SpecsLogs; /** - * Class with utility methods. - * - * @author JoaoBispo + * Utility class with static methods for jOptions operations. * + * @author JoaoBispo */ public class JOptionsUtils { /** * Helper method which uses this class as the class for the jar path. * - * @param optionsFilename - * @param storeDefinition - * @return + * @param optionsFilename the name of the options file + * @param storeDefinition the definition of the data store + * @return the loaded DataStore instance */ public static DataStore loadDataStore(String optionsFilename, StoreDefinition storeDefinition) { return loadDataStore(optionsFilename, JOptionsUtils.class, storeDefinition); @@ -51,11 +50,11 @@ public static DataStore loadDataStore(String optionsFilename, StoreDefinition st /** * Helper method which uses standard XmlPersistence as the default AppPersistence. * - * @param optionsFilename - * @param storeDefinition - * @return + * @param optionsFilename the name of the options file + * @param classForJarPath the class used to determine the jar path + * @param storeDefinition the definition of the data store + * @return the loaded DataStore instance */ - public static DataStore loadDataStore(String optionsFilename, Class classForJarPath, StoreDefinition storeDefinition) { XmlPersistence persistence = new XmlPersistence(storeDefinition); @@ -64,7 +63,7 @@ public static DataStore loadDataStore(String optionsFilename, Class classForJ } /** - * * Loads a DataStore file, from predefined places. + * Loads a DataStore file from predefined locations. * *

* The method will look for the file in the following places:
@@ -72,21 +71,18 @@ public static DataStore loadDataStore(String optionsFilename, Class classForJ * 2) In the current working folder
* *

- * If finds the file in multiple locations, cumulatively adds the options to the final DataStore. If the file in the - * jar path is not found, it is created. + * If the file is found in multiple locations, options are cumulatively added to the final DataStore. If the file in + * the jar path is not found, it is created. * - * - * @param optionsFilename - * @param classForJarPath - * @param storeDefinition - * @param persistence - * @return + * @param optionsFilename the name of the options file + * @param classForJarPath the class used to determine the jar path + * @param storeDefinition the definition of the data store + * @param persistence the persistence mechanism to use + * @return the loaded DataStore instance */ public static DataStore loadDataStore(String optionsFilename, Class classForJarPath, StoreDefinition storeDefinition, AppPersistence persistence) { - // DataStore localData = DataStore.newInstance(storeDefinition); - // Look for options in two places, JAR folder and current folder DataStore localData = loadOptionsNearJar(classForJarPath, optionsFilename, storeDefinition, persistence); @@ -105,15 +101,13 @@ public static DataStore loadDataStore(String optionsFilename, Class classForJ } /** - * Tries to load a + * Tries to load options near the JAR file. * - * @param optionsFilename - * @param jarFolder - * @param localData - * @param storeDefinition - * @param persistence - * - * @return the options file that was used, if found + * @param classForJarpath the class used to determine the jar path + * @param optionsFilename the name of the options file + * @param storeDefinition the definition of the data store + * @param persistence the persistence mechanism to use + * @return the loaded DataStore instance */ private static DataStore loadOptionsNearJar(Class classForJarpath, String optionsFilename, StoreDefinition storeDefinition, AppPersistence persistence) { @@ -150,17 +144,23 @@ else if (SpecsIo.canWriteFolder(jarFolder)) { return localData; } + /** + * Saves the given DataStore to a file. + * + * @param file the file to save the DataStore to + * @param data the DataStore instance to save + */ public static void saveDataStore(File file, DataStore data) { XmlPersistence persistence = data.getStoreDefinitionTry().map(XmlPersistence::new).orElse(new XmlPersistence()); persistence.saveData(file, data); } /** - * Executes the application. If not args are passed, launches the GUI mode, otherwise executes the CLI mode. + * Executes the application. If no arguments are passed, launches the GUI mode; otherwise, executes the CLI mode. * - * @param app - * @param args - * @return + * @param app the application instance + * @param args the list of arguments + * @return the exit code (0 for success, -1 for failure) */ public static int executeApp(App app, List args) { @@ -175,6 +175,14 @@ public static int executeApp(App app, List args) { return success ? 0 : -1; } + /** + * Executes the application kernel. If no arguments are passed, launches the GUI mode; otherwise, executes the CLI + * mode. + * + * @param app the application kernel instance + * @param args the list of arguments + * @return the exit code (0 for success, -1 for failure) + */ public static int executeApp(AppKernel app, List args) { // Instantiate App from AppKernel return executeApp(App.newInstance(app), args); diff --git a/jOptions/src/org/suikasoft/jOptions/Options/FileList.java b/jOptions/src/org/suikasoft/jOptions/Options/FileList.java index 58b31447..4d4bb080 100644 --- a/jOptions/src/org/suikasoft/jOptions/Options/FileList.java +++ b/jOptions/src/org/suikasoft/jOptions/Options/FileList.java @@ -1,11 +1,11 @@ -/** +/* * Copyright 2013 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -29,8 +29,9 @@ import pt.up.fe.specs.util.utilities.StringList; /** + * Utility class for managing a list of files and their associated folder in a DataStore. + * * @author Joao Bispo - * */ public class FileList { @@ -38,96 +39,79 @@ public class FileList { private static final DataKey KEY_FILENAMES = KeyFactory.stringList("Filenames"); private final DataStore data; - // private final String extension; private static final String DATA_NAME = "FileList DataStore"; /** - * + * Constructs a new FileList with an empty DataStore. */ public FileList() { - // super(getStoreDefinition(optionName)); data = DataStore.newInstance(FileList.DATA_NAME); - // this.extension = extension; - - // SetupDefinition def = GenericSetupDefinition.newInstance(optionName, - // getFolderProvider().getOptionDefinition(), - // getFilenamesProvider().getOptionDefinition()); - // - // setSetupTable(SimpleSetup.newInstance(def)); } + /** + * Returns the StoreDefinition for FileList. + * + * @return the StoreDefinition + */ public static StoreDefinition getStoreDefinition() { return StoreDefinition.newInstance(FileList.DATA_NAME, FileList.KEY_FOLDER, FileList.KEY_FILENAMES); } + /** + * Returns the option name for the folder. + * + * @return the folder option name + */ public static String getFolderOptionName() { return "Folder"; } + /** + * Returns the option name for the filenames. + * + * @return the filenames option name + */ public static String getFilesOptionName() { return "Filenames"; } + /** + * Returns the list of files represented by this FileList. + * + * @return the list of files + */ public List getFiles() { - // SetupOptions setupData = getSetupTable().getA(); - - // Get base folder - File baseFolder = data.get(FileList.KEY_FOLDER); - - // // Get Folder object - // Folder folder = value(option, Folder.class); - // - // // Check if has parent folder to pass - // File parentFolder = null; - // if (setup.getSetupFile() != null) { - // parentFolder = setup.getSetupFile().getParentFolder(); - // } - // - // return folder.getFolder(parentFolder); - - // File baseFolder = setupData.folder(getFolderProvider()); - - // Get filenames - // - // StringList filenamesList = setupData.value(getFilenamesProvider(), StringList.class); - // List filenames = filenamesList.getStringList(); List filenames = data.get(FileList.KEY_FILENAMES).getStringList(); - - // If list of filenames is empty, and base folder is not null, add all - // files in base folder, by name - // if (filenames.isEmpty() && (baseFolder != null)) { - // List files = IoUtils.getFiles(baseFolder, extension); - // for (File file : files) { - // filenames.add(file.getName()); - // } - // } - - // Build files with full path - List files = SpecsFactory.newArrayList(); + List files = new ArrayList<>(); for (String fileName : filenames) { File file = new File(baseFolder, fileName); - // Verify is file exists + // Verify if file exists if (!file.isFile()) { SpecsLogs.msgInfo("Could not find file '" + file.getAbsolutePath() + "'"); continue; } - // Add folder + filename files.add(file); } return files; } + /** + * Decodes a string representation of a FileList into a FileList object. + * + * @param string the string representation + * @return the FileList object + */ public static FileList decode(String string) { String[] values = string.split(";"); if (values.length < 1) { throw new RuntimeException( - "Could not find a value in string '" + string + "'. Does it have a at least a singel ';'?"); + "Could not find a value in string '" + string + "'. Does it have at least a single ';'?"); } FileList fileList = new FileList(); @@ -143,6 +127,11 @@ public static FileList decode(String string) { return fileList; } + /** + * Returns a string representation of this FileList. + * + * @return the string representation + */ @Override public String toString() { StringBuilder builder = new StringBuilder(); @@ -157,21 +146,4 @@ public String toString() { return builder.toString(); } - - // public static StringCodec codec() { - // - // return StringCodec.newInstance(encoder, FileList::decode); - // } - - /* - public static OptionDefinitionProvider getFolderProvider() { - return () -> Folder.newOption(getFolderOptionName(), true); - - } - - public static OptionDefinitionProvider getFilenamesProvider() { - return () -> KeyFactory.stringListOld(getFilesOptionName()); - } - */ - } diff --git a/jOptions/src/org/suikasoft/jOptions/Options/MultipleChoice.java b/jOptions/src/org/suikasoft/jOptions/Options/MultipleChoice.java index 59581d47..a673e64c 100644 --- a/jOptions/src/org/suikasoft/jOptions/Options/MultipleChoice.java +++ b/jOptions/src/org/suikasoft/jOptions/Options/MultipleChoice.java @@ -1,11 +1,11 @@ -/** +/* * Copyright 2013 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -21,163 +21,98 @@ import pt.up.fe.specs.util.SpecsLogs; /** + * Utility class for managing a set of multiple choices, with support for aliases and current selection. + * * @author Joao Bispo - * */ public class MultipleChoice { private final List choices; /** - * Maps choices to indexes + * Maps choices and aliases to indexes */ private final Map choicesMap; private int currentChoice; + /** + * Constructs a MultipleChoice with the given choices and aliases. + * + * @param choices the list of valid choices + * @param alias a map of alias names to choice names + */ private MultipleChoice(List choices, Map alias) { this.choices = choices; choicesMap = SpecsFactory.newHashMap(); for (int i = 0; i < choices.size(); i++) { choicesMap.put(choices.get(i), i); } - // Add all alias for (String aliasName : alias.keySet()) { - // Get choice String choice = alias.get(aliasName); - - // Get index Integer index = choicesMap.get(choice); if (index == null) { - SpecsLogs.msgInfo("Could not find choice '" + choice + "' for alias '" - + aliasName + "'"); + SpecsLogs.msgInfo("Could not find choice '" + choice + "' for alias '" + aliasName + "'"); continue; } - - // Add alias choicesMap.put(aliasName, index); } - currentChoice = 0; } + /** + * Creates a new MultipleChoice instance with the given choices and no aliases. + * + * @param choices the list of valid choices + * @return a new MultipleChoice instance + */ public static MultipleChoice newInstance(List choices) { Map emptyMap = Collections.emptyMap(); return newInstance(choices, emptyMap); } + /** + * Creates a new MultipleChoice instance with the given choices and aliases. + * + * @param choices the list of valid choices + * @param alias a map of alias names to choice names + * @return a new MultipleChoice instance + */ public static MultipleChoice newInstance(List choices, Map alias) { - // Check if number of choices is at least one if (choices.isEmpty()) { - throw new RuntimeException( - "MultipleChoice needs at least one choice, passed an empty list."); + throw new RuntimeException("MultipleChoice needs at least one choice, passed an empty list."); } - return new MultipleChoice(choices, alias); } - // public static DataKey newOption(String optionName, List choices, - // String defaultChoice) { - // - // Map emptyMap = Collections.emptyMap(); - // - // return newOption(optionName, choices, defaultChoice, emptyMap); - // } - - // public static DataKey newOption(String optionName, List choices, - // String defaultChoice, Map alias) { - // - // String defaultString = getDefaultString(choices, defaultChoice); - // String helpString = OptionUtils.getHelpString(optionName, MultipleChoice.class, defaultString); - // - // DataKey definition = new GenericOptionDefinition(optionName, MultipleChoice.class, helpString) - // .setDefault(() -> newInstance(choices).setChoice(defaultChoice)) - // .setDecoder(value -> new MultipleChoice(choices, alias).setChoice(value)); - // - // return definition; - // } - - // private static String getDefaultString(List choices, String defaultChoice) { - // StringBuilder builder = new StringBuilder(); - // - // builder.append(defaultChoice).append(" ["); - // if (!choices.isEmpty()) { - // builder.append(choices.get(0)); - // } - // - // for (int i = 1; i < choices.size(); i++) { - // builder.append(", ").append(choices.get(i)); - // } - // - // builder.append("]"); - // - // String defaultString = builder.toString(); - // return defaultString; - // } - - // /** - // * Define multiple choices through an enumeration. - // * - // *

- // * The enumeration class can optionally implement the interface AliasProvider, if the rules for enumeration names - // * are too limiting, or if several alias for the same choice are needed. - // * - // * @param optionName - // * @param aClass - // * @param defaultValue - // * @return - // */ - // public static > DataKey newOption(String optionName, - // Class aClass, T defaultValue) { - // - // List choices = EnumUtils.buildList(aClass.getEnumConstants()); - // - // Map alias = Collections.emptyMap(); - // - // // Check if class implements AliasProvider - // T anEnum = EnumUtils.getFirstEnum(aClass); - // if (anEnum instanceof AliasProvider) { - // alias = ((AliasProvider) anEnum).getAlias(); - // } - // - // return newOption(optionName, choices, defaultValue.name(), alias); - // } - + /** + * Sets the current choice to the specified value. + * + * @param choice the choice to set as current + * @return the updated MultipleChoice instance + */ public MultipleChoice setChoice(String choice) { - // Get index Integer index = choicesMap.get(choice); - if (index == null) { - SpecsLogs.warn("Choice '" + choice + "' not available. Available choices:" - + choices); + SpecsLogs.warn("Choice '" + choice + "' not available. Available choices:" + choices); return this; } - currentChoice = index; - return this; } - // private static ValueConverter getConverter(final List choices, - // final Map alias) { - // return new ValueConverter() { - // - // @Override - // public Object convert(String value) { - // MultipleChoice newObject = new MultipleChoice(choices, alias); - // newObject.setChoice(value); - // - // return newObject; - // } - // }; - // - // } - + /** + * Gets the current choice. + * + * @return the current choice + */ public String getChoice() { return choices.get(currentChoice); } - /* (non-Javadoc) - * @see java.lang.Object#toString() + /** + * Returns the string representation of the current choice. + * + * @return the current choice as a string */ @Override public String toString() { diff --git a/jOptions/src/org/suikasoft/jOptions/Utils/EnumCodec.java b/jOptions/src/org/suikasoft/jOptions/Utils/EnumCodec.java index 51b9018f..b9664d9b 100644 --- a/jOptions/src/org/suikasoft/jOptions/Utils/EnumCodec.java +++ b/jOptions/src/org/suikasoft/jOptions/Utils/EnumCodec.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.Utils; @@ -19,28 +19,49 @@ import pt.up.fe.specs.util.parsing.StringCodec; +/** + * StringCodec implementation for encoding and decoding Java enums. + * + * @param the enum type + */ public class EnumCodec> implements StringCodec { private final Class anEnum; private final Map decodeMap; private final Function encoder; + /** + * Creates an EnumCodec using the enum's toString() method for encoding. + * + * @param anEnum the enum class + */ public EnumCodec(Class anEnum) { - // this(anEnum, value -> value.name()); this(anEnum, value -> value.toString()); } + /** + * Creates an EnumCodec with a custom encoder function. + * + * @param anEnum the enum class + * @param encoder function to encode enum values to string + */ public EnumCodec(Class anEnum, Function encoder) { this.anEnum = anEnum; this.decodeMap = new HashMap<>(); this.encoder = encoder; for (T enumValue : anEnum.getEnumConstants()) { - // decodeMap.put(enumValue.toString(), enumValue); decodeMap.put(encoder.apply(enumValue), enumValue); } } + /** + * Decodes a string value to the corresponding enum constant. + * + * @param value the string value + * @return the enum constant + * @throws RuntimeException if the value does not match any enum constant + */ @Override public T decode(String value) { if (value == null) { @@ -48,7 +69,6 @@ public T decode(String value) { } T enumValue = decodeMap.get(value); - if (enumValue == null) { throw new RuntimeException("Could not find enum '" + value + "' in class '" + anEnum + "'. Available values: " + decodeMap.keySet()); @@ -57,10 +77,14 @@ public T decode(String value) { return enumValue; } + /** + * Encodes an enum constant to its string representation. + * + * @param value the enum constant + * @return the string representation + */ @Override public String encode(T value) { return encoder.apply(value); - // return value.toString(); } - } diff --git a/jOptions/src/org/suikasoft/jOptions/Utils/GuiHelperConverter.java b/jOptions/src/org/suikasoft/jOptions/Utils/GuiHelperConverter.java index dcac84a8..bf65fc12 100644 --- a/jOptions/src/org/suikasoft/jOptions/Utils/GuiHelperConverter.java +++ b/jOptions/src/org/suikasoft/jOptions/Utils/GuiHelperConverter.java @@ -34,15 +34,28 @@ import pt.up.fe.specs.util.exceptions.NotImplementedException; /** + * Utility class for converting GUI helper objects in jOptions. * Converts enums that implement {@link SetupFieldEnum} to StoreDefinition. */ public class GuiHelperConverter { + /** + * Converts a list of setup classes to a list of StoreDefinitions. + * + * @param setups the setup classes to convert + * @return a list of StoreDefinitions + */ public static & SetupFieldEnum> List toStoreDefinition( @SuppressWarnings("unchecked") Class... setups) { return toStoreDefinition(Arrays.asList(setups)); } + /** + * Converts a list of setup classes to a list of StoreDefinitions. + * + * @param setups the setup classes to convert + * @return a list of StoreDefinitions + */ public static & SetupFieldEnum> List toStoreDefinition(List> setups) { var converter = new GuiHelperConverter(); @@ -55,6 +68,12 @@ public static & SetupFieldEnum> List toStore return definitions; } + /** + * Converts a single setup class to a StoreDefinition. + * + * @param setup the setup class to convert + * @return the StoreDefinition + */ public & SetupFieldEnum> StoreDefinition convert(Class setup) { var name = setup.getSimpleName(); var keys = getDataKeys(setup.getEnumConstants()); @@ -62,6 +81,12 @@ public & SetupFieldEnum> StoreDefinition convert(Class set return StoreDefinition.newInstance(name, keys); } + /** + * Converts an array of setup keys to a list of DataKeys. + * + * @param setupKeys the setup keys to convert + * @return a list of DataKeys + */ public & SetupFieldEnum> List> getDataKeys( @SuppressWarnings("unchecked") T... setupKeys) { var keys = new ArrayList>(); @@ -72,6 +97,12 @@ public & SetupFieldEnum> List> getDataKeys( return keys; } + /** + * Converts a single setup key to a DataKey. + * + * @param setupKey the setup key to convert + * @return the DataKey + */ public & SetupFieldEnum> DataKey getDataKey(T setupKey) { var key = getBaseDataKey(setupKey); @@ -89,6 +120,12 @@ public & SetupFieldEnum> DataKey getDataKey(T setupKey) { return key; } + /** + * Gets the base DataKey for a setup key. + * + * @param setupKey the setup key + * @return the base DataKey + */ private & SetupFieldEnum> DataKey getBaseDataKey(T setupKey) { switch (setupKey.getType()) { case string: @@ -99,6 +136,13 @@ private & SetupFieldEnum> DataKey getBaseDa } + /** + * Converts a SetupList to a ListOfSetups. + * + * @param setupList the SetupList to convert + * @param tasksList the list of task classes + * @return the ListOfSetups + */ public static & SetupFieldEnum> ListOfSetups toListOfSetups(SetupList setupList, List> tasksList) { @@ -141,6 +185,12 @@ public static & SetupFieldEnum> ListOfSetups toListOfSetups(S return new ListOfSetups(listOfSetups); } + /** + * Gets the setup fields for a task class. + * + * @param taskList the task class + * @return a map of setup field names to SetupFieldEnum objects + */ private static & SetupFieldEnum> Map getSetupFields(Class taskList) { var taskKeys = new HashMap(); diff --git a/jOptions/src/org/suikasoft/jOptions/Utils/MultiEnumCodec.java b/jOptions/src/org/suikasoft/jOptions/Utils/MultiEnumCodec.java index e1d17f75..0192e7a7 100644 --- a/jOptions/src/org/suikasoft/jOptions/Utils/MultiEnumCodec.java +++ b/jOptions/src/org/suikasoft/jOptions/Utils/MultiEnumCodec.java @@ -22,6 +22,8 @@ import pt.up.fe.specs.util.parsing.StringCodec; /** + * Codec for handling multiple enums in jOptions. + * * @deprecated * @author JoaoBispo * @@ -35,6 +37,11 @@ public class MultiEnumCodec> implements StringCodec> { private final Class anEnum; private final Map decodeMap; + /** + * Constructor for MultiEnumCodec. + * + * @param anEnum the class of the enum type + */ public MultiEnumCodec(Class anEnum) { this.anEnum = anEnum; this.decodeMap = new HashMap<>(); @@ -44,6 +51,12 @@ public MultiEnumCodec(Class anEnum) { } } + /** + * Decodes a string into a list of enum values. + * + * @param value the string to decode + * @return a list of decoded enum values + */ @Override public List decode(String value) { List decodedValues = new ArrayList<>(); @@ -71,6 +84,12 @@ private T decodeSingle(String value) { return enumValue; } + /** + * Encodes a list of enum values into a string. + * + * @param value the list of enum values to encode + * @return the encoded string + */ @Override public String encode(List value) { return value.stream() diff --git a/jOptions/src/org/suikasoft/jOptions/Utils/MultipleChoiceListCodec.java b/jOptions/src/org/suikasoft/jOptions/Utils/MultipleChoiceListCodec.java index 711f0213..8d8b5538 100644 --- a/jOptions/src/org/suikasoft/jOptions/Utils/MultipleChoiceListCodec.java +++ b/jOptions/src/org/suikasoft/jOptions/Utils/MultipleChoiceListCodec.java @@ -20,27 +20,33 @@ import pt.up.fe.specs.util.parsing.StringCodec; /** + * Codec for handling multiple choice lists in jOptions. + * * @author JoaoBispo * - * @param + * @param the type of elements in the list */ public class MultipleChoiceListCodec implements StringCodec> { private static final String SEPARATOR = "$$$"; - // private final Class anEnum; - // private final Map decodeMap; private final StringCodec elementCodec; + /** + * Constructs a MultipleChoiceListCodec with the given element codec. + * + * @param elementCodec the codec for individual elements + */ public MultipleChoiceListCodec(StringCodec elementCodec) { - // this.anEnum = anEnum; - // this.decodeMap = new HashMap<>(); this.elementCodec = elementCodec; - // for (T enumValue : anEnum.getEnumConstants()) { - // decodeMap.put(enumValue.name(), enumValue); - // } } + /** + * Decodes a string into a list of elements. + * + * @param value the string to decode + * @return a list of decoded elements + */ @Override public List decode(String value) { List decodedValues = new ArrayList<>(); @@ -58,16 +64,14 @@ public List decode(String value) { private T decodeSingle(String value) { return elementCodec.decode(value); - // T enumValue = decodeMap.get(value); - // - // if (enumValue == null) { - // throw new RuntimeException("Could not find enum '" + value + "' in class '" + anEnum - // + "'. Available values: " + decodeMap.keySet()); - // } - // - // return enumValue; } + /** + * Encodes a list of elements into a string. + * + * @param value the list of elements to encode + * @return the encoded string + */ @Override public String encode(List value) { return value.stream() diff --git a/jOptions/src/org/suikasoft/jOptions/Utils/RawValueUtils.java b/jOptions/src/org/suikasoft/jOptions/Utils/RawValueUtils.java index 0f3f7559..03de7224 100644 --- a/jOptions/src/org/suikasoft/jOptions/Utils/RawValueUtils.java +++ b/jOptions/src/org/suikasoft/jOptions/Utils/RawValueUtils.java @@ -13,6 +13,10 @@ package org.suikasoft.jOptions.Utils; +/** + * Utility class for handling raw values in jOptions. + */ + import org.suikasoft.jOptions.Datakey.DataKey; import pt.up.fe.specs.util.SpecsLogs; @@ -38,8 +42,9 @@ public class RawValueUtils { * - Tries to find a default converter in the table.
* - Returns null. * - * @param optionDef - * @param rawValue + * @param optionDef the DataKey definition of the option + * @param rawValue the raw value in String format + * @return the converted value, or null if no valid converter is found */ public static Object getRealValue(DataKey optionDef, String value) { diff --git a/jOptions/src/org/suikasoft/jOptions/Utils/SetupFile.java b/jOptions/src/org/suikasoft/jOptions/Utils/SetupFile.java index 48c8689d..b2167b15 100644 --- a/jOptions/src/org/suikasoft/jOptions/Utils/SetupFile.java +++ b/jOptions/src/org/suikasoft/jOptions/Utils/SetupFile.java @@ -18,6 +18,8 @@ import pt.up.fe.specs.util.SpecsIo; /** + * Utility class for setup file operations in jOptions. + * * TODO: Rename to ConfigFile * * @author JoaoBispo @@ -27,15 +29,29 @@ public class SetupFile { private File setupFile; + /** + * Default constructor. Initializes the setup file to null. + */ public SetupFile() { setupFile = null; } + /** + * Sets the setup file. + * + * @param setupFile the file to set + * @return the current instance of SetupFile + */ public SetupFile setFile(File setupFile) { this.setupFile = setupFile; return this; } + /** + * Gets the setup file. + * + * @return the setup file + */ public File getFile() { return setupFile; } @@ -43,7 +59,7 @@ public File getFile() { /** * If no setup file is defined, returns the current work folder. * - * @return + * @return the parent folder of the setup file, or the current work folder if no setup file is defined */ public File getParentFolder() { if (setupFile == null) { @@ -60,7 +76,7 @@ public File getParentFolder() { } /** - * + * Resets the setup file to null. */ public void resetFile() { setupFile = null; diff --git a/jOptions/src/org/suikasoft/jOptions/app/App.java b/jOptions/src/org/suikasoft/jOptions/app/App.java index 761b6678..cdc554f5 100644 --- a/jOptions/src/org/suikasoft/jOptions/app/App.java +++ b/jOptions/src/org/suikasoft/jOptions/app/App.java @@ -8,7 +8,7 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.app; @@ -25,15 +25,23 @@ import pt.up.fe.specs.util.providers.ResourceProvider; /** - * @author Joao Bispo + * Interface for application definitions in jOptions. + * Provides methods for kernel access, app name, options, and tab providers. * + * @author Joao Bispo */ @FunctionalInterface public interface App { + /** + * Returns the kernel for this app. + * + * @return the app kernel + */ AppKernel getKernel(); /** + * Returns the name of the app. * * @return name of the app */ @@ -43,11 +51,10 @@ default String getName() { /** * The options available for this app. - * *

* By default, creates a StoreDefinition from the DataKeys in the AppKernel class. - * - * @return + * + * @return the store definition */ default StoreDefinition getDefinition() { return StoreDefinition.newInstanceFromInterface(getClass()); @@ -56,20 +63,35 @@ default StoreDefinition getDefinition() { /** * The interface for loading and storing configurations. * - * @return + * @return the persistence mechanism */ default AppPersistence getPersistence() { return new XmlPersistence(getDefinition()); } + /** + * Returns a collection of tab providers for the app GUI. + * + * @return collection of tab providers + */ default Collection getOtherTabs() { return Collections.emptyList(); } + /** + * Returns the class of the app node. + * + * @return the node class + */ default Class getNodeClass() { return getClass(); } + /** + * Returns an optional resource provider for the app icon. + * + * @return optional resource provider for icon + */ default Optional getIcon() { return Optional.empty(); } @@ -77,11 +99,11 @@ default Optional getIcon() { /** * Creates a new App. * - * @param name - * @param definition - * @param persistence - * @param kernel - * @return + * @param name the name of the app + * @param definition the store definition + * @param persistence the persistence mechanism + * @param kernel the app kernel + * @return a new GenericApp instance */ static GenericApp newInstance(String name, StoreDefinition definition, AppPersistence persistence, AppKernel kernel) { @@ -89,12 +111,26 @@ static GenericApp newInstance(String name, StoreDefinition definition, return new GenericApp(name, definition, persistence, kernel); } + /** + * Creates a new App using the store definition name. + * + * @param definition the store definition + * @param persistence the persistence mechanism + * @param kernel the app kernel + * @return a new GenericApp instance + */ static GenericApp newInstance(StoreDefinition definition, AppPersistence persistence, AppKernel kernel) { return newInstance(definition.getName(), definition, persistence, kernel); } + /** + * Creates a new App using the kernel. + * + * @param kernel the app kernel + * @return a new App instance + */ static App newInstance(AppKernel kernel) { var storeDefinition = StoreDefinition.newInstanceFromInterface(kernel.getClass()); return newInstance(storeDefinition, new XmlPersistence(storeDefinition), kernel); diff --git a/jOptions/src/org/suikasoft/jOptions/app/AppDefaultConfig.java b/jOptions/src/org/suikasoft/jOptions/app/AppDefaultConfig.java index 0bfd1055..6b74ef28 100644 --- a/jOptions/src/org/suikasoft/jOptions/app/AppDefaultConfig.java +++ b/jOptions/src/org/suikasoft/jOptions/app/AppDefaultConfig.java @@ -1,25 +1,29 @@ /* * Copyright 2011 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.app; /** - * When the Preferences for a config file cannot be read at program launch (i.e. first execution) this interface can - * provide a default file. - * + * Provides a default configuration file when preferences cannot be read at program launch (e.g., first execution). + * * @author Joao Bispo */ public interface AppDefaultConfig { + /** + * Returns the path to the default configuration file. + * + * @return the default config file path + */ String defaultConfigFile(); } diff --git a/jOptions/src/org/suikasoft/jOptions/app/AppKernel.java b/jOptions/src/org/suikasoft/jOptions/app/AppKernel.java index a476a268..2613c9ca 100644 --- a/jOptions/src/org/suikasoft/jOptions/app/AppKernel.java +++ b/jOptions/src/org/suikasoft/jOptions/app/AppKernel.java @@ -16,6 +16,8 @@ import org.suikasoft.jOptions.Interfaces.DataStore; /** + * Kernel class for jOptions applications. + * * @author Joao Bispo * */ @@ -23,8 +25,10 @@ public interface AppKernel { /** * The main method of the app. + * Executes the application with the given options. * - * @return + * @param options the configuration options for the application + * @return an integer representing the result of the execution */ int execute(DataStore options); } diff --git a/jOptions/src/org/suikasoft/jOptions/app/AppPersistence.java b/jOptions/src/org/suikasoft/jOptions/app/AppPersistence.java index 74aaa565..0a94cab3 100644 --- a/jOptions/src/org/suikasoft/jOptions/app/AppPersistence.java +++ b/jOptions/src/org/suikasoft/jOptions/app/AppPersistence.java @@ -18,28 +18,37 @@ import org.suikasoft.jOptions.Interfaces.DataStore; /** + * Persistence utilities for jOptions applications. + * * @author Joao Bispo * */ public interface AppPersistence { + /** + * Loads data from the specified file. + * + * @param file the file to load data from + * @return the loaded data as a DataStore object + */ public DataStore loadData(File file); /** + * Saves data to the specified file. * - * @param file - * @param setup - * @param keepSetupFile - * @return + * @param file the file to save data to + * @param data the data to be saved + * @param keepConfigFile whether to keep the configuration file path in the persistent format + * @return true if the data was successfully saved, false otherwise */ public boolean saveData(File file, DataStore data, boolean keepConfigFile); /** * Helper method which does not save the config file path in the persistent format. * - * @param file - * @param data - * @return + * @param file the file to save data to + * @param data the data to be saved + * @return true if the data was successfully saved, false otherwise */ default boolean saveData(File file, DataStore data) { return saveData(file, data, false); diff --git a/jOptions/src/org/suikasoft/jOptions/app/FileReceiver.java b/jOptions/src/org/suikasoft/jOptions/app/FileReceiver.java index 943ac1ff..72572687 100644 --- a/jOptions/src/org/suikasoft/jOptions/app/FileReceiver.java +++ b/jOptions/src/org/suikasoft/jOptions/app/FileReceiver.java @@ -1,21 +1,31 @@ -/** - * Copyright 2015 SPeCS. - * +/* + * Copyright 2015 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.app; import java.io.File; +/** + * Interface for classes that receive files in jOptions applications. + * + * @author Joao Bispo + */ public interface FileReceiver { + /** + * Receives a file. + * + * @param file the file to receive + */ void updateFile(File file); } diff --git a/jOptions/src/org/suikasoft/jOptions/arguments/ArgumentsParser.java b/jOptions/src/org/suikasoft/jOptions/arguments/ArgumentsParser.java index 8f154bbf..dc1d65b4 100644 --- a/jOptions/src/org/suikasoft/jOptions/arguments/ArgumentsParser.java +++ b/jOptions/src/org/suikasoft/jOptions/arguments/ArgumentsParser.java @@ -1,14 +1,14 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.arguments; @@ -34,6 +34,9 @@ import pt.up.fe.specs.util.collections.MultiMap; import pt.up.fe.specs.util.parsing.ListParser; +/** + * Parses and manages command-line arguments for jOptions-based applications. + */ public class ArgumentsParser { /** @@ -42,17 +45,26 @@ public class ArgumentsParser { private static final DataKey SHOW_HELP = KeyFactory.bool("arguments_parser_show_help") .setLabel("Shows this help message"); + /** + * Executes the program using the given file representing a serialized DataStore instance. + */ private static final DataKey DATASTORE_FILE = KeyFactory.file("arguments_parser_datastore_file") .setLabel("Executes the program using the given file representing a serialized DataStore instance"); + /** + * Executes the program using the given text file containing command-line options. + */ private static final DataKey CONFIG_FILE = KeyFactory.file("arguments_parser_config_file") - .setLabel("Executes the program using the given text file containig command-line options"); + .setLabel("Executes the program using the given text file containing command-line options"); private final Map, DataStore>> parsers; private final MultiMap, String> datakeys; private final Map, Integer> consumedArgs; private final Set ignoreFlags; + /** + * Constructs an ArgumentsParser instance and initializes default parsers and flags. + */ public ArgumentsParser() { parsers = new LinkedHashMap<>(); datakeys = new MultiMap<>(() -> new LinkedHashMap<>()); @@ -64,6 +76,13 @@ public ArgumentsParser() { ignoreFlags.add("//"); } + /** + * Executes the application kernel with the parsed arguments. + * + * @param kernel the application kernel to execute + * @param args the list of command-line arguments + * @return the exit code of the application + */ public int execute(AppKernel kernel, List args) { DataStore config = parse(args); @@ -76,20 +95,12 @@ public int execute(AppKernel kernel, List args) { return kernel.execute(config); } + /** + * Prints the help message for the command-line arguments, listing all supported flags and their descriptions. + */ private void printHelpMessage() { StringBuilder message = new StringBuilder(); - // message.append("EclipseBuild - Generates and runs ANT scripts for Eclipse Java projects\n\n"); - // message.append("Usage: [-i ] [-u ] [-i...\n\n"); - // message.append( - // "Default files that will be searched for in the root of the repository folders if no flag is specified:\n"); - // message.append(" ").append(getDefaultUserLibraries()).append(" - Eclipse user libraries\n"); - // message.append(" ").append(getDefaultIvySettingsFile()).append(" - Ivy settings file\n"); - // message.append(" ").append(getDefaultIgnoreProjectsFile()) - // .append(" - Text file with list of projects to ignore (one project name per line)\n"); - // - // message.append("\nAdditional options:\n"); for (DataKey key : datakeys.keySet()) { - // for (String key : parsers.keySet()) { String flags = datakeys.get(key).stream().collect(Collectors.joining(", ")); message.append(" ").append(flags); @@ -109,6 +120,12 @@ private void printHelpMessage() { SpecsLogs.msgInfo(message.toString()); } + /** + * Parses the given list of command-line arguments into a DataStore instance. + * + * @param args the list of command-line arguments + * @return a DataStore instance containing the parsed arguments + */ public DataStore parse(List args) { DataStore parsedData = DataStore.newInstance("ArgumentsParser Data"); @@ -139,81 +156,76 @@ public DataStore parse(List args) { } /** - * Adds a boolean key. - * - * @param key - * @param flags - * @return + * Adds a boolean key to the parser, associating it with the given flags. + * + * @param key the DataKey representing the boolean value + * @param flags the flags associated with the key + * @return the updated ArgumentsParser instance */ public ArgumentsParser addBool(DataKey key, String... flags) { return addPrivate(key, list -> true, 0, flags); - // for (String flag : flags) { - // parsers.put(flag, addValue(key, true)); - // } - // - // return this; } /** - * Adds a key that uses the next argument as value. - * - * @param key - * @param flags - * @return + * Adds a key that uses the next argument as a value, associating it with the given flags. + * + * @param key the DataKey representing the string value + * @param flags the flags associated with the key + * @return the updated ArgumentsParser instance */ public ArgumentsParser addString(DataKey key, String... flags) { return addPrivate(key, list -> list.popSingle(), 1, flags); - // for (String flag : flags) { - // parsers.put(flag, addValueFromList(key, ListParser::popSingle)); - // } - // - // return this; } /** - * Uses the key's decoder to parse the next argument. - * - * @param key - * @param flags - * @return + * Uses the key's decoder to parse the next argument, associating it with the given flags. + * + * @param key the DataKey representing the value + * @param flags the flags associated with the key + * @param the value type + * @return the updated ArgumentsParser instance */ public ArgumentsParser add(DataKey key, String... flags) { return add(key, list -> key.getDecoder().get().decode(list.popSingle()), 1, flags); - - // for (String flag : flags) { - // parsers.put(flag, (list, dataStore) -> dataStore.add(key, key.getDecoder().get().decode(list.popSingle()))); - // } - // - // return this; } /** - * Accepts a custom parser for the next argument. - * - * @param key - * @param parser - * @param flags - * @return + * Accepts a custom parser for the next argument, associating it with the given flags. + * + * @param key the DataKey representing the value + * @param parser the custom parser function + * @param consumedArgs the number of arguments consumed by the parser + * @param flags the flags associated with the key + * @param the value type + * @return the updated ArgumentsParser instance */ - @SuppressWarnings("unchecked") // Unchecked cases are verified + @SuppressWarnings("unchecked") public ArgumentsParser add(DataKey key, Function, V> parser, Integer consumedArgs, String... flags) { // Check if value of the key is of type Boolean if (key.getValueClass().equals(Boolean.class)) { return addBool((DataKey) key, flags); - // return addPrivate((DataKey) key, list -> true, 0, flags); } // Check if value of the key is of type String if (key.getValueClass().equals(String.class)) { return addString((DataKey) key, flags); - // return addPrivate((DataKey) key, list -> true, 0, flags); } return addPrivate(key, parser, consumedArgs, flags); } + /** + * Adds a key with a custom parser and flags (internal helper). + * + * @param key the DataKey representing the value + * @param parser the custom parser function + * @param consumedArgs the number of arguments consumed by the parser + * @param flags the flags associated with the key + * @param the value type + * @return the updated ArgumentsParser instance + */ private ArgumentsParser addPrivate(DataKey key, Function, V> parser, Integer consumedArgs, String... flags) { @@ -223,8 +235,6 @@ private ArgumentsParser addPrivate(DataKey key, Function dataStore.add(key, parser.apply(list))); - // datakeys.put(flag, key); - // this.consumedArgs.put(flag, consumedArgs); } datakeys.put(key, Arrays.asList(flags)); @@ -233,6 +243,12 @@ private ArgumentsParser addPrivate(DataKey key, Function BiConsumer, DataStore> addValue(DataKey key, V value) { - // return (list, dataStore) -> dataStore.add(key, value); - // } - - /** - * Helper method for options that consume parameters. - * - * @param key - * @param processArgs - * @return - */ - // private static BiConsumer, DataStore> addValueFromList(DataKey key, - // Function, V> processArgs) { - // - // return (list, dataStore) -> { - // V value = processArgs.apply(list); - // dataStore.add(key, value); - // }; - // - // } - } diff --git a/jOptions/src/org/suikasoft/jOptions/cli/AppLauncher.java b/jOptions/src/org/suikasoft/jOptions/cli/AppLauncher.java index 23ad2bf8..6c75f6ef 100644 --- a/jOptions/src/org/suikasoft/jOptions/cli/AppLauncher.java +++ b/jOptions/src/org/suikasoft/jOptions/cli/AppLauncher.java @@ -1,14 +1,14 @@ /** * Copyright 2013 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.cli; @@ -27,109 +27,62 @@ import pt.up.fe.specs.util.SpecsLogs; /** - * @author Joao Bispo - * + * Utility class for launching jOptions-based applications from the command line. */ public class AppLauncher { private final App app; - // private final AppKernel app; - // private final String appName; - // private final SetupDefinition setupDefition; - private final List resources; - // private final Map, Object> defaultValues; - private File baseFolder; - // private final AppPersistence persistence; - - /* - public AppLauncher(AppKernel app, String appName, - Class> setupDefinitionClass, - AppPersistence persistence) { - - this(app, appName, getDefinition(setupDefinitionClass), persistence); - } - */ - /* - private static SetupDefinition getDefinition( - Class> setupDefinitionClass) { - Enum enums[] = setupDefinitionClass.getEnumConstants(); - if (enums.length == 0) { - throw new RuntimeException("Given enum class '" + setupDefinitionClass - + "' has zero enums."); - } - - return ((SetupProvider) enums[0]).getSetupDefinition(); - } - */ /** - * @param app + * Constructs an AppLauncher instance for the given application. + * + * @param app the application to be launched */ - // public AppLauncher(AppKernel app, String appName, SetupDefinition setupDefinition, - // AppPersistence persistence) { public AppLauncher(App app) { - this.app = app; - // this.app = app; - // this.appName = appName; - // this.setupDefition = setupDefinition; - resources = SpecsFactory.newArrayList(); - // defaultValues = CommandLineUtils.getDefaultValues(); - - // persistence = new XmlPersistence(setupDefinition.getOptions()); - // this.persistence = persistence; baseFolder = null; } + /** + * Adds resources to the launcher. + * + * @param resources a collection of resource paths + */ public void addResources(Collection resources) { this.resources.addAll(resources); } /** - * @return the app + * Retrieves the application associated with this launcher. + * + * @return the application instance */ public App getApp() { return app; } /** - * @return the appName - */ - /* - public String getAppName() { - return appName; - } - */ - - /** - * Helper method with String array. - * - * @param args - * @return + * Launches the application with the given arguments. + * + * @param args an array of command-line arguments + * @return true if the application launched successfully, false otherwise */ public boolean launch(String[] args) { return launch(Arrays.asList(args)); } /** - * Parse the input arguments looking for a configuration file in the first argument. - * - *

- * If found, parses other arguments as key-value pairs, to be replaced in the given setup file, and launches the - * program returning true upon completion. - * - * If the first argument is not a configuration file, applies default values to the non-defined parameters. - * - * @param args - * @return + * Launches the application with the given arguments. + * + * @param args a list of command-line arguments + * @return true if the application launched successfully, false otherwise */ public boolean launch(List args) { if (args.isEmpty()) { - SpecsLogs - .msgInfo("No arguments found. Please enter a configuration file, or key/value pairs."); + SpecsLogs.msgInfo("No arguments found. Please enter a configuration file, or key/value pairs."); return false; } @@ -154,8 +107,10 @@ public boolean launch(List args) { } /** - * @param args - * @return + * Parses special arguments such as base folder configuration. + * + * @param args a list of command-line arguments + * @return the modified list of arguments */ private List parseSpecialArguments(List args) { // If first argument is base_folder="path", create temporary file there and remove option @@ -171,41 +126,30 @@ private List parseSpecialArguments(List args) { } /** - * Adds a default value for options of the given class. - * - * @param aClass - * @param value + * Launches the application in command-line mode without a setup file. + * + * @param args a list of command-line arguments + * @return true if the application launched successfully, false otherwise */ - /* - public void addDefaultValue(Class aClass, Object value) { - Object previousValue = defaultValues.get(aClass); - if (previousValue != null) { - LoggingUtils.msgInfo("Replacing previous default value for class '" - + aClass.getSimpleName() + "': " + previousValue + " -> " + value); - } - - defaultValues.put(aClass, value); - } - */ - private boolean launchCommandLineNoSetup(List args) { - // Create empty setup data DataStore data = DataStore.newInstance(app.getDefinition()); - // SimpleSetup setupData = SimpleSetup.newInstance(app.getDefinition(), defaultValues); - // Execute command-line mode commandLineWithSetup(args, data); return true; } + /** + * Executes the application with the given setup data and arguments. + * + * @param args a list of command-line arguments + * @param setupData the setup data for the application + */ private void commandLineWithSetup(List args, DataStore setupData) { - new CommandLineUtils(app.getDefinition()).addArgs(setupData, args); - // File tempFile = new File(baseFolder, app.getClass().getSimpleName() + "__temp_config.xml"); File tempFile = new File(baseFolder, app.getClass().getSimpleName() + "__temp_config.data"); app.getPersistence().saveData(tempFile, setupData, true); @@ -218,6 +162,12 @@ private void commandLineWithSetup(List args, DataStore setupData) { tempFile.delete(); } + /** + * Executes the application with the given setup file. + * + * @param setupFile the setup file containing configuration data + * @return the result of the application execution + */ public int execute(File setupFile) { DataStore setupData = app.getPersistence().loadData(setupFile); diff --git a/jOptions/src/org/suikasoft/jOptions/cli/CommandLineUtils.java b/jOptions/src/org/suikasoft/jOptions/cli/CommandLineUtils.java index d4717c55..c7d65361 100644 --- a/jOptions/src/org/suikasoft/jOptions/cli/CommandLineUtils.java +++ b/jOptions/src/org/suikasoft/jOptions/cli/CommandLineUtils.java @@ -1,14 +1,14 @@ /** * Copyright 2013 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.cli; @@ -27,8 +27,7 @@ import pt.up.fe.specs.util.SpecsLogs; /** - * @author Joao Bispo - * + * Utility methods for parsing and handling command-line arguments for jOptions-based applications. */ public class CommandLineUtils { @@ -37,13 +36,20 @@ public class CommandLineUtils { private final StoreDefinition definition; + /** + * Constructs a CommandLineUtils instance with the given store definition. + * + * @param definition the store definition to be used for parsing command-line arguments + */ public CommandLineUtils(StoreDefinition definition) { this.definition = definition; } /** - * @param arg - * @return + * Parses the value from a key-value argument string. + * + * @param arg the key-value argument string + * @return the parsed value */ private static String parseValue(String arg) { int index = arg.indexOf("="); @@ -51,24 +57,12 @@ private static String parseValue(String arg) { return value; } - // /** - // * @param arg - // * @return - // */ - // private static List parseKey(String arg) { - // int index = arg.indexOf("="); - // if (index == -1) { - // LoggingUtils.msgInfo("Problem in key-value '" + arg - // + "'. Check if key-value is separated by a '='."); - // return null; - // } - // - // String keyString = arg.substring(0, index); - // List key = Arrays.asList(keyString.split("/")); - // - // return key; - // } - + /** + * Parses the key from a key-value argument string. + * + * @param arg the key-value argument string + * @return the parsed key, or null if the argument is invalid + */ private static String parseSimpleKey(String arg) { int index = arg.indexOf("="); if (index == -1) { @@ -81,24 +75,12 @@ private static String parseSimpleKey(String arg) { } /** - * @return + * Launches an application in command-line mode. + * + * @param app the application to be launched + * @param args the command-line arguments + * @return true if the application was successfully launched or a special command was processed, false otherwise */ - /* - public static Map, Object> getDefaultValues() { - return CommandLineUtils.DEFAULT_VALUES; - } - **/ - - /** - * Launches an application on command-line mode. - * - * @param app - * @param args - */ - // public static boolean launch(App app, String... args) { - // return launch(app, Arrays.asList(args)); - // } - public static boolean launch(App app, List args) { // Check for some special commands @@ -108,7 +90,6 @@ public static boolean launch(App app, List args) { } // If at least one argument, launch application. - // if (args.length > 0) { if (!args.isEmpty()) { AppLauncher launcher = new AppLauncher(app); return launcher.launch(args); @@ -122,16 +103,13 @@ public static boolean launch(App app, List args) { } /** - * @param app - * @param args - * @return + * Processes special commands such as "write" or "--help". + * + * @param app the application instance + * @param args the command-line arguments + * @return true if a special command was processed, false otherwise */ - // private static boolean processSpecialCommands(App app, String... args) { - // return processSpecialCommands(app, Arrays.asList(args)); - // } - private static boolean processSpecialCommands(App app, List args) { - // if (args.length == 0) { if (args.isEmpty()) { return false; } @@ -163,6 +141,12 @@ private static boolean processSpecialCommands(App app, List args) { return false; } + /** + * Adds command-line arguments to the given DataStore. + * + * @param setupData the DataStore to be updated + * @param args the command-line arguments + */ public void addArgs(DataStore setupData, List args) { // Iterate over each argument for (String arg : args) { @@ -195,30 +179,15 @@ public void addArgs(DataStore setupData, List args) { // Set value setupData.setRaw(key, value); - - // setValue(setupData, keyString, stringValue, definition); - - // // Discover type of key - // DataKey key = OptionUtils.getKey(setupData, keyString); - // // Option option = OptionUtils.getOption(setupData, keyString); - // if (key == null) { - // LoggingUtils.msgInfo("Could not find option with key '" + keyString + "'"); - // if (setupData.getStoreDefinition().isPresent()) { - // LoggingUtils.msgInfo("Base Keys:" + setupData.getStoreDefinition().get().getKeys()); - // } - // - // continue; - // } - // - // // Get key to reach setup - // List keyToSetup = keyString.subList(0, keyString.size() - 1); - // - // // Set option - // OptionUtils.setRawOption(setupData, keyToSetup, key, stringValue); } - } + /** + * Generates a help message for the given store definition. + * + * @param setupDef the store definition + * @return the help message + */ public static String getHelp(StoreDefinition setupDef) { StringBuilder builder = new StringBuilder(); @@ -229,13 +198,21 @@ public static String getHelp(StoreDefinition setupDef) { return builder.toString(); } + /** + * Generates a help message for the given store definition. + * + * @param setupDefinition the store definition + * @return the help message + */ private static String getHelpString(StoreDefinition setupDefinition) { return getHelp(setupDefinition.getKeys()); } /** - * @param setupDef - * @return + * Generates a help message for the given collection of DataKeys. + * + * @param optionDefs the collection of DataKeys + * @return the help message */ private static String getHelp(Collection> optionDefs) { StringBuilder builder = new StringBuilder(); @@ -252,65 +229,4 @@ private static String getHelp(Collection> optionDefs) { return builder.toString(); } - - // private static void setValue(DataStore setup, List keyString, String stringValue, - // StoreDefinition definition) { - // - // Preconditions.checkArgument(!keyString.isEmpty(), "Passed empty key string"); - // - // // Get key corresponding to the string - // DataKey key = definition.getKeyMap().get(keyString.get(0)); - // if (key == null) { - // LoggingUtils.msgInfo("Key '" + keyString.get(0) + "' not found in store definition '" - // + definition.getName() + "'. Keys: " + definition.getKeys()); - // return; - // } - // - // // If only one string, set value - // if (keyString.size() == 1) { - // - // // Decode value - // if (!key.getDecoder().isPresent()) { - // LoggingUtils.msgInfo("No decoder found for key '" + key + "'"); - // return; - // } - // - // Object value = key.getDecoder().get().decode(stringValue); - // - // setup.setRaw(key, value); - // return; - // } - // - // // If key has more than one string: - // // 1) Next key must return a StoreDefinition - // // 2) Key must store a DataStoreProvider - // if (!key.getStoreDefinition().isPresent()) { - // LoggingUtils.msgInfo("Key '" + key + "' is part of a chain, must define a StoreDefinition"); - // return; - // } - // - // Optional valueTry = setup.getTry(key); - // - // // If no value yet, invoke constructor from Definition and put back into setup - // if (!valueTry.isPresent()) { - // DataStoreProvider provider = key.getStoreDefinition().get().getStore(); - // setup.setRaw(key, provider); - // - // valueTry = Optional.of(provider); - // } - // - // Object value = valueTry.get(); - // - // if (!(value instanceof DataStoreProvider)) { - // LoggingUtils.msgInfo("Key '" + key + "' is part of a chain, corresponding value of class '" - // + key.getValueClass() + "' must implement DataStoreProvider"); - // return; - // } - // - // StoreDefinition nextDefinition = key.getStoreDefinition().get(); - // DataStore nextDataStore = ((DataStoreProvider) value).getDataStore(); - // List nextKeyString = keyString.subList(1, keyString.size()); - // - // setValue(nextDataStore, nextKeyString, stringValue, nextDefinition); - // } } diff --git a/jOptions/src/org/suikasoft/jOptions/cli/GenericApp.java b/jOptions/src/org/suikasoft/jOptions/cli/GenericApp.java index f44c7eaa..7d39e3e8 100644 --- a/jOptions/src/org/suikasoft/jOptions/cli/GenericApp.java +++ b/jOptions/src/org/suikasoft/jOptions/cli/GenericApp.java @@ -8,7 +8,7 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.cli; @@ -28,8 +28,7 @@ import pt.up.fe.specs.util.providers.ResourceProvider; /** - * @author Joao Bispo - * + * Generic implementation of the {@link App} interface for jOptions-based applications. */ public class GenericApp implements App { @@ -41,6 +40,17 @@ public class GenericApp implements App { private final Class nodeClass; private final ResourceProvider icon; + /** + * Constructs a GenericApp instance with the specified parameters. + * + * @param name the name of the application + * @param definition the store definition for the application + * @param persistence the persistence mechanism for the application + * @param kernel the kernel of the application + * @param otherTabs additional tabs to be displayed in the application + * @param nodeClass the class representing the node structure + * @param icon the icon resource for the application + */ private GenericApp(String name, StoreDefinition definition, AppPersistence persistence, AppKernel kernel, Collection otherTabs, Class nodeClass, ResourceProvider icon) { @@ -54,75 +64,137 @@ private GenericApp(String name, StoreDefinition definition, this.icon = icon; } + /** + * Constructs a GenericApp instance with the specified parameters, using default values for otherTabs, nodeClass, and icon. + * + * @param name the name of the application + * @param definition the store definition for the application + * @param persistence the persistence mechanism for the application + * @param kernel the kernel of the application + */ public GenericApp(String name, StoreDefinition definition, AppPersistence persistence, AppKernel kernel) { this(name, definition, persistence, kernel, Collections.emptyList(), null, null); } + /** + * Constructs a GenericApp instance with the specified parameters, using XmlPersistence as the default persistence mechanism. + * + * @param name the name of the application + * @param definition the store definition for the application + * @param kernel the kernel of the application + */ public GenericApp(String name, StoreDefinition definition, AppKernel kernel) { this(name, definition, new XmlPersistence(definition), kernel, Collections.emptyList(), null, null); } - /* (non-Javadoc) - * @see org.suikasoft.jOptions.CommandLine.Interfaces.App#getDefinition() + /** + * Gets the store definition of the application. + * + * @return the store definition */ @Override public StoreDefinition getDefinition() { return definition; } - /* (non-Javadoc) - * @see org.suikasoft.jOptions.CommandLine.Interfaces.App#getPersistence() + /** + * Gets the persistence mechanism of the application. + * + * @return the persistence mechanism */ @Override public AppPersistence getPersistence() { return persistence; } - /* (non-Javadoc) - * @see org.suikasoft.jOptions.CommandLine.Interfaces.App#getKernel() + /** + * Gets the kernel of the application. + * + * @return the kernel */ @Override public AppKernel getKernel() { return kernel; } - /* (non-Javadoc) - * @see org.suikasoft.jOptions.CommandLine.Interfaces.App#getName() + /** + * Gets the name of the application. + * + * @return the name */ @Override public String getName() { return name; } + /** + * Gets the additional tabs to be displayed in the application. + * + * @return a collection of tab providers + */ @Override public Collection getOtherTabs() { return otherTabs; } + /** + * Gets the icon resource for the application. + * + * @return an optional containing the icon resource, or empty if not set + */ @Override public Optional getIcon() { return Optional.ofNullable(icon); } + /** + * Sets additional tabs for the application. + * + * @param otherTabs a collection of tab providers + * @return a new GenericApp instance with the updated tabs + */ public GenericApp setOtherTabs(Collection otherTabs) { return new GenericApp(name, definition, persistence, kernel, otherTabs, nodeClass, icon); } + /** + * Sets additional tabs for the application. + * + * @param otherTabs an array of tab providers + * @return a new GenericApp instance with the updated tabs + */ public GenericApp setOtherTabs(TabProvider... otherTabs) { return setOtherTabs(Arrays.asList(otherTabs)); } + /** + * Sets the node class for the application. + * + * @param nodeClass the class representing the node structure + * @return a new GenericApp instance with the updated node class + */ public GenericApp setNodeClass(Class nodeClass) { return new GenericApp(name, definition, persistence, kernel, otherTabs, nodeClass, icon); } + /** + * Sets the icon resource for the application. + * + * @param icon the icon resource + * @return a new GenericApp instance with the updated icon + */ public GenericApp setIcon(ResourceProvider icon) { return new GenericApp(name, definition, persistence, kernel, otherTabs, nodeClass, icon); } + /** + * Gets the node class for the application. + * + * @return the node class, or the class of this instance if not set + */ @Override public Class getNodeClass() { if (nodeClass == null) { diff --git a/jOptions/src/org/suikasoft/jOptions/gui/AppFrame.java b/jOptions/src/org/suikasoft/jOptions/gui/AppFrame.java index 5d48e2e4..c054f598 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/AppFrame.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/AppFrame.java @@ -27,9 +27,9 @@ import org.suikasoft.jOptions.gui.panels.app.TabbedPane; /** - * Frame of the SimpleGui. + * Frame of the SimpleGui application. * - * @author Joao Bispo + *

This class manages the main application window, tabbed pane, and GUI launching for the application. */ public class AppFrame { @@ -43,6 +43,11 @@ public class AppFrame { public static final int PREFERRED_HEIGHT = 360; public static final int PREFERRED_WIDTH = 560; + /** + * Constructs an AppFrame for the given application. + * + * @param application the application to display + */ public AppFrame(App application) { frameTitle = application.getName(); tabbedPane = new TabbedPane(application); @@ -56,21 +61,33 @@ public AppFrame(App application) { } } + /** + * Returns the TabbedPane instance. + * + * @return the TabbedPane + */ public TabbedPane getTabbedPane() { return tabbedPane; } + /** + * Sets the frame title. + * + * @param frameTitle the title to set + */ public void setFrameTitle(String frameTitle) { mainWindow.setTitle(frameTitle); } + /** + * Launches the GUI in the event dispatch thread. + */ public void launchGui() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // Turn off metal's use of bold fonts UIManager.put("swing.boldMetal", Boolean.FALSE); - // createAndShowGUI(); showGui(); } }); @@ -79,34 +96,20 @@ public void run() { /** * Shows the GUI. For thread safety, this method should be invoked from the event dispatch thread. */ - // private void createAndShowGUI() { private void showGui() { - /* - //Create and set up the window. - JFrame frame = new JFrame(frameTitle); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - frame.setResizable(true); - //Add content to the window. - frame.add(tabbedPane, BorderLayout.CENTER); - */ - // Display the window. - // frame.pack(); - // frame.setVisible(true); mainWindow.pack(); mainWindow.setVisible(true); } /** * Creates the GUI. + * + * @return the JFrame representing the main application window */ private JFrame createGui() { // Create and set up the window. - // JFrame frame = new JFrame(frameTitle); - // mainWindow = new JFrame(frameTitle); JFrame frame = new JFrame(frameTitle); - // JFrame frame = mainWindow; frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setResizable(true); @@ -119,6 +122,11 @@ private JFrame createGui() { return frame; } + /** + * Returns the main application window. + * + * @return the JFrame representing the main application window + */ public JFrame getMainWindow() { return mainWindow; } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/ApplicationWorker.java b/jOptions/src/org/suikasoft/jOptions/gui/ApplicationWorker.java index 2633baa6..29c41f2d 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/ApplicationWorker.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/ApplicationWorker.java @@ -26,7 +26,9 @@ import pt.up.fe.specs.util.SpecsSwing; /** - * Launches an App object from the ProgramPanel. + * Launches an App object from the ProgramPanel, managing execution in a separate thread. + * + *

This class provides methods to execute an application asynchronously and handle its lifecycle in the GUI. * * TODO: Extract Runnable ApplicationRunner from this class. * @@ -34,6 +36,11 @@ */ public class ApplicationWorker { + /** + * Constructs an ApplicationWorker for the given ProgramPanel. + * + * @param programPanel the ProgramPanel to use + */ public ApplicationWorker(ProgramPanel programPanel) { mainWindow = programPanel; workerExecutor = null; @@ -41,8 +48,8 @@ public ApplicationWorker(ProgramPanel programPanel) { /** * Executes the application in another thread. - * - * @param options + * + * @param options the DataStore with options for execution */ public void execute(DataStore options) { @@ -53,19 +60,14 @@ public void execute(DataStore options) { } /** - * To be run on Monitor thread, so the Gui is not waiting for the result of task. + * To be run on Monitor thread, so the GUI is not waiting for the result of task. * - * @param options + * @param setup the DataStore setup */ private void runner(DataStore setup) { // Disable buttons setButtons(false); - // Save SecurityManager - // SecurityManager previousManager = System.getSecurityManager(); - // Set SecurityManager that catches System.exit() calls - // System.setSecurityManager(new SecurityManagerNoExit()); - // Create task Callable task = getTask(setup); @@ -85,19 +87,11 @@ private void runner(DataStore setup) { } } - // Restore SecurityManager - // System.setSecurityManager(previousManager); - if (result == null) { SpecsLogs.msgInfo("Application execution could not proceed."); - // LoggingUtils.getLogger(). - // info("Cancelled application."); - // info("Application was cancelled."); } else if (result.compareTo(0) != 0) { SpecsLogs.msgInfo("*Application Stopped*"); SpecsLogs.msgLib("Worker return value: " + result); - // LoggingUtils.getLogger(). - // info("Application returned non-zero value:" + result); } // Enable buttons again @@ -105,6 +99,11 @@ private void runner(DataStore setup) { } + /** + * Enables or disables buttons in the GUI. + * + * @param enable true to enable buttons, false to disable + */ private void setButtons(final boolean enable) { SpecsSwing.runOnSwing(new Runnable() { @@ -117,14 +116,18 @@ public void run() { } /** - * Builds a task out of the application - * - * @return + * Builds a task out of the application. + * + * @param setup the DataStore setup + * @return a Callable task for execution */ private Callable getTask(DataStore setup) { return () -> mainWindow.getApplication().getKernel().execute(setup); } + /** + * Shuts down the worker executor, stopping any running tasks. + */ public void shutdown() { if (workerExecutor == null) { SpecsLogs.getLogger().warning("Application is not running."); @@ -134,32 +137,21 @@ public void shutdown() { workerExecutor.shutdownNow(); } + /** + * Displays an exception message in the logs. + * + * @param ex the ExecutionException to log + */ private static void showExceptionMessage(ExecutionException ex) { - String prefix = " happend while executing the application"; + String prefix = " happened while executing the application"; Throwable ourCause = ex.getCause(); - // String prefix = ourCause.toString() + if (ourCause == null) { - // LoggingUtils.getLogger(). - // info("\nAn Exception" + prefix + ", but could not get cause."); SpecsLogs.warn("\nAn Exception" + prefix + ", but could not get cause."); } else { - // LoggingUtils.msgInfo("\n"+ourCause.toString()); SpecsLogs.msgInfo(""); - // LoggingUtils.msgInfo(ourCause.getMessage()); SpecsLogs.warn(ourCause.toString(), ourCause); - /* - LoggingUtils.msgInfo("\nPrinting the stack trace:"); - //info("\n"+ourCause.toString() + prefix + ". Printing the stack trace:\n"); - StackTraceElement[] trace = ourCause.getStackTrace(); - //LoggingUtils.getLogger(). - // info(ourCause.toString()); - for (int i = 0; i < trace.length; i++) { - LoggingUtils.getLogger(). - info("\tat " + trace[i]); - } - */ } } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/KeyPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/KeyPanel.java index 1bbfa6ba..b48a19c5 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/KeyPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/KeyPanel.java @@ -22,92 +22,90 @@ import org.suikasoft.jOptions.Interfaces.DataStore; /** - * A GUI panel that returns a value of a type. - * - * @author João Bispo + * A GUI panel that returns a value of a type for a DataKey. + * + *

This abstract class provides the base for panels that interact with DataKeys and DataStores in the GUI. * - * @param + * @param the type of value handled by the panel */ public abstract class KeyPanel extends JPanel { private final DataKey key; private final DataStore data; - // private T value; - // private final long lastTime; - /** * */ private static final long serialVersionUID = 1L; + /** + * Constructs a KeyPanel for the given DataKey and DataStore. + * + * @param key the DataKey + * @param data the DataStore + */ protected KeyPanel(DataKey key, DataStore data) { - this.key = key; - this.data = data; - // this.value = value; - - // this.lastTime = -1; + this.key = key; + this.data = data; } /** - * Creates a panel using the key's default value. - *

- * Will throw an exception if key does not have a default value set. - * - * @param key + * Returns the current value that the panel has. + * + * @return the current value */ - // protected KeyPanel(DataKey key) { - // this(key, key.getDefaultValueV2() - // .orElseThrow(() -> new RuntimeException("No default defined for key '" + key.getName() + "'"))); - // } + public abstract T getValue(); /** - * - * @return the current value that panel has. + * Returns the DataKey associated with this panel. + * + * @return the DataKey */ - public abstract T getValue(); - // return value; - // } - public DataKey getKey() { - return this.key; + return this.key; } + /** + * Returns the DataStore associated with this panel. + * + * @return the DataStore + */ public DataStore getData() { - return data; + return data; } /** * Stores the value in the panel in the given DataStore, using the corresponding key. - * - * @param data + * + * @param data the DataStore to store the value in */ public void store(DataStore data) { - data.set(getKey(), getValue()); + data.set(getKey(), getValue()); } /** - * Updates the Panel with the given value + * Updates the panel with the given value. * - * @param option + * @param value the value to set + * @param the type of value (extends T) */ public abstract void setValue(ET value); /** * The default label name is the name of the key. - * + * * @return the default label name */ protected String getDefaultLabelName() { - return getKey().getName(); + return getKey().getName(); } /** * The nested panels this panel has, if any. By default, returns an empty list. * - * @return + * @return a collection of nested panels */ public Collection> getPanels() { - return Collections.emptyList(); + return Collections.emptyList(); } } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/KeyPanelProvider.java b/jOptions/src/org/suikasoft/jOptions/gui/KeyPanelProvider.java index 65817aa9..316c1a12 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/KeyPanelProvider.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/KeyPanelProvider.java @@ -16,7 +16,18 @@ import org.suikasoft.jOptions.Datakey.DataKey; import org.suikasoft.jOptions.Interfaces.DataStore; +/** + * Provider interface for creating KeyPanel instances for a given DataKey and DataStore. + * + * @param the type of value handled by the panel + */ public interface KeyPanelProvider { - + /** + * Returns a KeyPanel for the given DataKey and DataStore. + * + * @param key the DataKey + * @param data the DataStore + * @return a KeyPanel for the key and data + */ KeyPanel getPanel(DataKey key, DataStore data); } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/SimpleGui.java b/jOptions/src/org/suikasoft/jOptions/gui/SimpleGui.java index 1d027f0b..a89c93e2 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/SimpleGui.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/SimpleGui.java @@ -8,7 +8,7 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.gui; @@ -18,18 +18,28 @@ import org.suikasoft.jOptions.app.App; /** - * Wrapper around AppFrame. + * Wrapper around AppFrame for launching and managing the application GUI. * - * @author Joao Bispo + *

This class provides a simple interface to start and control the main application window. */ public class SimpleGui { private final AppFrame frame; + /** + * Constructs a SimpleGui for the given application. + * + * @param application the application to launch + */ public SimpleGui(App application) { frame = new AppFrame(application); } + /** + * Returns the AppFrame instance. + * + * @return the AppFrame + */ public AppFrame getAppFrame() { return frame; } @@ -47,10 +57,20 @@ public void run() { }); } + /** + * Sets the window title. + * + * @param windowTitle the title to set + */ public void setTitle(String windowTitle) { frame.setFrameTitle(windowTitle); } + /** + * Returns the main JFrame window. + * + * @return the main JFrame + */ public JFrame getFrame() { return frame.getMainWindow(); } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/AppKeys.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/AppKeys.java index 839205e3..71a34dfe 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/AppKeys.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/AppKeys.java @@ -18,7 +18,15 @@ import org.suikasoft.jOptions.Datakey.DataKey; import org.suikasoft.jOptions.Datakey.KeyFactory; +/** + * Common DataKeys for application configuration. + * + *

This interface defines standard DataKeys used in application panels. + */ public interface AppKeys { + /** + * DataKey for the application configuration file. + */ DataKey CONFIG_FILE = KeyFactory.file("app_config"); } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/BaseSetupPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/BaseSetupPanel.java index ee366392..8be23c23 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/BaseSetupPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/BaseSetupPanel.java @@ -33,8 +33,9 @@ import pt.up.fe.specs.util.SpecsLogs; /** - * Panel which will contain the options + * Panel which contains the options for a setup, organizing KeyPanels for each DataKey. * + *

This panel arranges option panels for each DataKey in a StoreDefinition, supporting indentation and sectioning. * * @author Joao Bispo */ @@ -44,20 +45,29 @@ public class BaseSetupPanel extends JPanel { private final Map> panels; private final StoreDefinition storeDefinition; - // private final DataStore data; - - // private final int identationLevel; public static final int IDENTATION_SIZE = 6; + /** + * Constructs a BaseSetupPanel for the given StoreDefinition and DataStore. + * + * @param keys the StoreDefinition + * @param data the DataStore + */ public BaseSetupPanel(StoreDefinition keys, DataStore data) { this(keys, data, 0); } + /** + * Constructs a BaseSetupPanel for the given StoreDefinition, DataStore, and indentation level. + * + * @param keys the StoreDefinition + * @param data the DataStore + * @param identationLevel the indentation level + */ public BaseSetupPanel(StoreDefinition keys, DataStore data, int identationLevel) { storeDefinition = keys; panels = new HashMap<>(); - // this.data = data; if (keys == null) { throw new RuntimeException("StoreDefinition is null."); @@ -94,7 +104,6 @@ public BaseSetupPanel(StoreDefinition keys, DataStore data, int identationLevel) separatorC.gridy = labelC.gridy; add(new javax.swing.JSeparator(), separatorC); - // add(new javax.swing.JSeparator(), panelC); labelC.gridy++; panelC.gridy++; @@ -115,7 +124,6 @@ public BaseSetupPanel(StoreDefinition keys, DataStore data, int identationLevel) JLabel label = new JLabel(key.getLabel() + ": "); add(label, labelC); - // add(new JLabel(": ")); add(panel, panelC); labelC.gridy++; @@ -136,10 +144,20 @@ public BaseSetupPanel(StoreDefinition keys, DataStore data, int identationLevel) add(new JPanel(), paddingVC); } + /** + * Returns the map of KeyPanels associated with their respective DataKey names. + * + * @return a map of KeyPanels + */ public Map> getPanels() { return panels; } + /** + * Loads values from the given DataStore into the KeyPanels. + * + * @param map the DataStore containing values to load + */ public void loadValues(DataStore map) { if (map == null) { map = DataStore.newInstance("empty_map"); @@ -148,24 +166,30 @@ public void loadValues(DataStore map) { for (DataKey key : storeDefinition.getKeys()) { Object value = getValue(map, key); KeyPanel panel = panels.get(key.getName()); - // getValue() will return a value compatible with the key, which is compatible with the keyPanel - // if (key.getName().equals("Print Clava Info")) { - // System.out.println("SETTING: " + key.getName()); - // System.out.println("VALUE: " + value); - // System.out.println("DEFAULT:" + key.getDefault()); - // System.out.println("MAP:" + map); - // } - uncheckedSet(panel, value); } } + /** + * Sets the value of a KeyPanel without type checking. + * + * @param the type of the KeyPanel + * @param panel the KeyPanel + * @param o the value to set + */ @SuppressWarnings("unchecked") private static void uncheckedSet(KeyPanel panel, Object o) { panel.setValue((T) o); } + /** + * Retrieves the value for a DataKey from the given DataStore. + * + * @param map the DataStore + * @param key the DataKey + * @return the value for the DataKey + */ private static Object getValue(DataStore map, DataKey key) { Optional value = map.getTry(key); @@ -173,12 +197,7 @@ private static Object getValue(DataStore map, DataKey key) { return value.get(); } - if (key.getName().equals("Print Clava Info")) { - System.out.println("NOT PRESENT"); - } - // Return default value - // This section of code was commented, do not know why. Was there some kind of problem? if (key.getDefault().isPresent()) { Object defaultValue = key.getDefault().get(); SpecsLogs.msgInfo("Could not find a value for option '" + key.getName() + "', using default value '" @@ -194,28 +213,19 @@ private static Object getValue(DataStore map, DataKey key) { } throw new RuntimeException("Could not get a value for key '" + key - + "', please define a default value or a decoder thta supports 'null' string as input"); + + "', please define a default value or a decoder that supports 'null' string as input"); } /** * Collects information in all the panels and returns a DataStore with the information. * - * @return + * @return a DataStore containing the collected information */ public DataStore getData() { DataStore dataStore = DataStore.newInstance(storeDefinition); for (KeyPanel panel : panels.values()) { panel.store(dataStore); - // panel.getValue(); - // AKeyPanel panel = panels.get(key); - // FieldValue value = panel.getOption(); - // if (value == null) { - // LoggingUtils.getLogger().warning("value is null."); - // // No valid value for the table - // continue; - // } - // updatedMap.put(key, value); } return dataStore; diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/GuiTab.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/GuiTab.java index 0ab3ef38..eda0115d 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/GuiTab.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/GuiTab.java @@ -18,27 +18,51 @@ import org.suikasoft.jOptions.Interfaces.DataStore; /** - * @author Joao Bispo + * Abstract base class for tabs in the application GUI. + * + *

This class provides a contract for tabs that interact with a DataStore and require enter/exit lifecycle methods. * + * @author Joao Bispo */ public abstract class GuiTab extends JPanel { private final DataStore data; + /** + * Constructs a GuiTab with the given DataStore. + * + * @param data the DataStore for the tab + */ public GuiTab(DataStore data) { this.data = data; } + /** + * Returns the DataStore associated with this tab. + * + * @return the DataStore + */ public DataStore getData() { return data; } private static final long serialVersionUID = 1L; + /** + * Called when entering the tab. + */ public abstract void enterTab(); + /** + * Called when exiting the tab. + */ public abstract void exitTab(); + /** + * Returns the name of the tab. + * + * @return the tab name + */ public abstract String getTabName(); } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/OptionsPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/OptionsPanel.java index a9fd41d6..2b7e0677 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/OptionsPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/OptionsPanel.java @@ -39,6 +39,8 @@ /** * Panel which loads and can edit the options file. + * + *

This panel provides controls for loading, editing, and saving application options. * * @author Joao Bispo */ @@ -49,7 +51,6 @@ public class OptionsPanel extends GuiTab { private final App app; private final BaseSetupPanel setupPanel; - // private DataStore optionsData; private final JButton saveButton; private final JButton saveAsButton; private final JLabel fileInfo; @@ -57,6 +58,12 @@ public class OptionsPanel extends GuiTab { private File outputFile; + /** + * Constructs an OptionsPanel for the given application and DataStore. + * + * @param app the application instance + * @param data the DataStore + */ public OptionsPanel(App app, DataStore data) { super(data); this.app = app; @@ -72,15 +79,10 @@ public OptionsPanel(App app, DataStore data) { optionsPanel.setPreferredSize(new Dimension(AppFrame.PREFERRED_WIDTH + 10, AppFrame.PREFERRED_HEIGHT + 10)); optionsPanel.setViewportView(setupPanel); - // JComponent optionsPanel = initEnumOptions(definition); - - // optionsData = DataStore.newInstance(app.getDefinition()); fileInfo = new JLabel(); updateFileInfoString(); saveButton = new JButton("Save"); - // By default, the save button is disable, until there is a valid - // file to save to. saveButton.setEnabled(false); saveAsButton = new JButton("Save as..."); @@ -93,9 +95,6 @@ public OptionsPanel(App app, DataStore data) { savePanel.add(saveAsButton); savePanel.add(fileInfo); - // If optionFile no file, save button is null; - // Only "unnulls" after "Save As..." and after successful update. - setLayout(new BorderLayout(5, 5)); add(savePanel, BorderLayout.PAGE_START); @@ -103,17 +102,21 @@ public OptionsPanel(App app, DataStore data) { } + /** + * Retrieves the panels associated with the setup. + * + * @return a map of panel names to KeyPanel objects + */ public Map> getPanels() { return setupPanel.getPanels(); } - // public DataStore getOptionFile() { - // return optionsData; - // } - + /** + * Handles the action performed when the save button is clicked. + * + * @param evt the action event + */ private void saveButtonActionPerformed(ActionEvent evt) { - // updateInternalMap(); - // optionsData = setupPanel.getData(); if (outputFile == null) { saveAsButtonActionPerformed(evt); return; @@ -122,60 +125,48 @@ private void saveButtonActionPerformed(ActionEvent evt) { app.getPersistence().saveData(outputFile, setupPanel.getData()); } + /** + * Handles the action performed when the save-as button is clicked. + * + * @param evt the action event + */ private void saveAsButtonActionPerformed(ActionEvent evt) { - // JFileChooser fc; - - // If no output file, choose current folder if (outputFile == null) { fileChooser.setCurrentDirectory(new File("./")); - } - // If output file exists, choose as folder - else if (outputFile.exists()) { + } else if (outputFile.exists()) { fileChooser.setCurrentDirectory(outputFile); - } - // Otherwise, use current folder as default - else { + } else { fileChooser.setCurrentDirectory(new File("./")); } - // app.getPersistence().saveData(outputFile, setupPanel.getData()); - int returnVal = fileChooser.showOpenDialog(this); if (returnVal == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); - // Files returned from this chooser cannot be folders outputFile = file; saveButton.setEnabled(true); updateFileInfoString(); - // Update current folder path getData().set(JOptionKeys.CURRENT_FOLDER_PATH, Optional.of(SpecsIo.getCanonicalFile(file).getParent())); - // getData().set(JOptionKeys.CURRENT_FOLDER_PATH, SpecsIo.getCanonicalFile(file).getParent()); - // updateFile(file); - // Automatically save data app.getPersistence().saveData(outputFile, setupPanel.getData()); } } - // public void updateValues(String optionsFilename) { + /** + * Updates the values in the setup panel with the given DataStore. + * + * @param map the DataStore containing the new values + */ public void updateValues(DataStore map) { - - // Load file - // optionsData = map; - setupPanel.loadValues(map); saveButton.setEnabled(true); updateFileInfoString(); - - // Update receivers - // for (FileReceiver fileReceiver : fileReceivers) { - // fileReceiver.updateFile(file); - // } - } + /** + * Updates the file information label with the current output file. + */ private void updateFileInfoString() { File file = outputFile; String filename; @@ -189,45 +180,26 @@ private void updateFileInfoString() { fileInfo.setText(text); } - /* - * Sets the current option file to the given file. - */ - // private void updateFile(File file) { - // // updateInternalMap(); - // // optionsData = setupPanel.getData(); - // outputFile = file; - // saveButton.setEnabled(true); - // updateFileInfoString(); - // - // } - - // private void updateInternalMap() { - // // Get info from panels - // optionsData = appFilePanel.getData(); - // - // // Update internal optionfile - // // optionsData = updatedMap; - // } - /** - * Can only be called after setup panels are initallized. - * - * @param newOptionFile + * Retrieves the current output file. + * + * @return the output file */ - // private void assignNewOptionFile(DataStore newOptionFile) { - // optionFile = newOptionFile; - // } - public File getOutputFile() { return outputFile; } + /** + * Sets the current output file. + * + * @param outputFile the output file to set + */ public void setOutputFile(File outputFile) { this.outputFile = outputFile; } - /* (non-Javadoc) - * @see pt.up.fe.specs.guihelper.gui.BasePanels.GuiTab#enterTab(pt.up.fe.specs.guihelper.Gui.BasePanels.TabData) + /** + * Called when entering the tab. Updates the setup panel with the current configuration. */ @Override public void enterTab() { @@ -241,20 +213,13 @@ public void enterTab() { } updateValues(map); - // File outputFile = getData().get(AppKeys.CONFIG_FILE); - // if (outputFile == null) { - // return; - // } - // - // if (outputFile.exists()) { - // setOutputFile(outputFile); - // } - // - // if (outputFile.isFile()) { - // updateValues(outputFile.getPath()); - // } } + /** + * Retrieves the DataStore based on the current configuration file. + * + * @return the DataStore + */ private DataStore getDataStore() { File outputFile = getData().get(AppKeys.CONFIG_FILE); if (outputFile == null) { @@ -271,7 +236,6 @@ private DataStore getDataStore() { String optionsFilename = outputFile.getPath(); - // Check if filename is a valid optionsfile File file = new File(optionsFilename); if (!file.isFile()) { SpecsLogs.getLogger().warning("Could not open file '" + optionsFilename + "'"); @@ -290,7 +254,6 @@ private DataStore getDataStore() { newMap = null; } - // SetupData newMap = GuiHelperUtils.loadData(file); if (newMap == null) { SpecsLogs.info("Given file '" + optionsFilename + "' is not a compatible options file."); outputFile = null; @@ -302,8 +265,8 @@ private DataStore getDataStore() { return newMap; } - /* (non-Javadoc) - * @see pt.up.fe.specs.guihelper.gui.BasePanels.GuiTab#exitTab(pt.up.fe.specs.guihelper.Gui.BasePanels.TabData) + /** + * Called when exiting the tab. Updates the configuration file in the DataStore. */ @Override public void exitTab() { @@ -313,14 +276,22 @@ public void exitTab() { } } - /* (non-Javadoc) - * @see pt.up.fe.specs.guihelper.gui.BasePanels.GuiTab#getTabName() + /** + * Retrieves the name of the tab. + * + * @return the tab name */ @Override public String getTabName() { return "Options"; } + /** + * Retrieves the panel associated with the given DataKey. + * + * @param key the DataKey + * @return the KeyPanel associated with the key + */ public KeyPanel getPanel(DataKey key) { var panel = getPanels().get(key.getName()); diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/ProgramPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/ProgramPanel.java index fa0a9162..65d08a4d 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/ProgramPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/ProgramPanel.java @@ -46,6 +46,8 @@ * Panel used to indicate the setup file and which can start and cancel the execution of the program. Also shows the * output of the program. * + *

This panel provides controls for selecting setup files, starting/cancelling execution, and displaying output. + * * @author Ancora Group */ public class ProgramPanel extends GuiTab { @@ -68,16 +70,19 @@ public class ProgramPanel extends GuiTab { private ApplicationWorker worker; private final LastUsedItems lastUsedItems; - // private static final String OPTION_LAST_USED_FILE = "lastUsedFile"; private static final String OPTION_LAST_USED_ITEMS = "lastUsedItems"; private static final int LAST_USED_ITEMS_CAPACITY = 10; - // Items will be filenames, and they should not have the character '?' private static final String ITEMS_SEPARATOR_REGEX = "\\?"; private static final String ITEMS_SEPARATOR = "?"; private static final String BLANK_OPTION_FILE = ""; - /** Creates new form ProgramPanel */ + /** + * Creates a new ProgramPanel for the given application and DataStore. + * + * @param application the application instance + * @param data the DataStore + */ public ProgramPanel(App application, DataStore data) { super(data); @@ -104,37 +109,23 @@ public ProgramPanel(App application, DataStore data) { // Set head of lastUsedItems as the item that appears in box Optional head = lastUsedItems.getHead(); if (head.isPresent()) { - // filenameTextField.setSelectedItem(head.get()); filenameTextField.getEditor().setItem(head.get()); - // filenameTextField.getEditor().setItem(firstItem); - // filenameTextField.setSelectedItem(firstItem); - // System.out.println("NOT SETTING? -> " + head.get()); - // System.out.println("NOT SETTING 2? -> " + firstItem); - // System.out.println("EQUAL? " + (firstItem == head.get())); } else { filenameTextField.getEditor().setItem(buildDefaultOptionFilename()); } - // String firstItem = items.isEmpty() ? null : items.get(0); - // System.out.println("FIRST ITEM:" + firstItem); - // filenameTextField.setSelectedItem(null); - // filenameTextField.setSelectedItem(firstItem); - repaint(); revalidate(); - // Dimension d = filenameTextField.getPreferredSize(); - // Dimension newD = new Dimension(100, (int) d.getHeight()); - // filenameTextField.setPreferredSize(newD); - - // String defaultOptionFile = buildDefaultOptionFilename(); - // filenameTextField.getEditor().setItem(defaultOptionFile); - - // showStackTrace = true; customInit(); - } + /** + * Parses the last used files string into a list of file paths. + * + * @param lastFilesString the string containing file paths separated by the separator + * @return a list of file paths + */ private static List parseLastFiles(String lastFilesString) { if (lastFilesString.isEmpty()) { return Collections.emptyList(); @@ -145,23 +136,8 @@ private static List parseLastFiles(String lastFilesString) { } /** - * @return the showStackTrace + * Performs custom initialization for the ProgramPanel. */ - /* - public boolean isShowStackTrace() { - return showStackTrace; - } - */ - /** - * @param showStackTrace - * the showStackTrace to set - */ - /* - public void setShowStackTrace(boolean showStackTrace) { - this.showStackTrace = showStackTrace; - } - */ - private void customInit() { // Init file chooser @@ -209,7 +185,6 @@ private void initComponents() { startButton.setText("Start"); startButton.addActionListener(evt -> startButtonActionPerformed(evt)); - // outputArea.setColumns(20); outputArea.setEditable(false); outputArea.setRows(15); outputArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14)); @@ -251,8 +226,12 @@ private void initComponents() { .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 155, Short.MAX_VALUE))); }// //GEN-END:initComponents - private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_browseButtonActionPerformed - // File optionsFile = new File(filenameTextField.getText()); + /** + * Handles the action performed when the browse button is clicked. + * + * @param evt the action event + */ + private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) { File optionsFile = new File(filenameTextField.getEditor().getItem().toString()); if (!optionsFile.exists()) { optionsFile = new File("./"); @@ -264,29 +243,34 @@ private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN File file = fc.getSelectedFile(); filenameTextField.getEditor().setItem(file.getAbsolutePath()); } - }// GEN-LAST:event_browseButtonActionPerformed - - private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_cancelButtonActionPerformed + } + /** + * Handles the action performed when the cancel button is clicked. + * + * @param evt the action event + */ + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) { worker.shutdown(); + } - }// GEN-LAST:event_cancelButtonActionPerformed - - private void startButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_startButtonActionPerformed + /** + * Handles the action performed when the start button is clicked. + * + * @param evt the action event + */ + private void startButtonActionPerformed(java.awt.event.ActionEvent evt) { execute(); - - }// GEN-LAST:event_startButtonActionPerformed + } /** - * + * Executes the application with the selected setup file. */ public void execute() { // Clear text area outputArea.setText(""); // Check if file is valid - // String filename = filenameTextField.getText(); - // String filename = filenameTextField.getSelectedItem().toString(); String filename = filenameTextField.getEditor().getItem().toString(); File file = new File(filename); @@ -310,7 +294,6 @@ public void execute() { prefs.put(ProgramPanel.OPTION_LAST_USED_ITEMS, lastUsedFilesString); // Update JComboBox - // OPT - It might be enough to remove just the last one, if the jcombobox is already full filenameTextField.removeAllItems(); for (String item : lastUsedItems.getItems()) { filenameTextField.addItem(item); @@ -321,24 +304,50 @@ public void execute() { worker.execute(setup); } + /** + * Encodes a list of file paths into a single string separated by the separator. + * + * @param lastUsedItems the list of file paths + * @return the encoded string + */ private static String encodeList(List lastUsedItems) { return lastUsedItems.stream().collect(Collectors.joining(ProgramPanel.ITEMS_SEPARATOR)); } + /** + * Enables or disables the buttons in the panel. + * + * @param enable true to enable the buttons, false to disable + */ public final void setButtonsEnable(boolean enable) { browseButton.setEnabled(enable); startButton.setEnabled(enable); cancelButton.setEnabled(!enable); } + /** + * Gets the application instance associated with this panel. + * + * @return the application instance + */ public App getApplication() { return application; } + /** + * Gets the filename text field component. + * + * @return the filename text field + */ public JComboBox getFilenameTextField() { return filenameTextField; } + /** + * Builds the default option filename for the application. + * + * @return the default option filename + */ private String buildDefaultOptionFilename() { // Check if App implements AppDefaultConfig if (!AppDefaultConfig.class.isInstance(application)) { @@ -348,8 +357,8 @@ private String buildDefaultOptionFilename() { return ((AppDefaultConfig) application).defaultConfigFile(); } - /* (non-Javadoc) - * @see pt.up.fe.specs.guihelper.gui.BasePanels.GuiTab#enterTab(pt.up.fe.specs.guihelper.Gui.BasePanels.TabData) + /** + * Called when entering the tab. Updates the filename text field with the current configuration file path. */ @Override public void enterTab() { @@ -361,12 +370,11 @@ public void enterTab() { getFilenameTextField().getEditor().setItem(file.getPath()); } - /* (non-Javadoc) - * @see pt.up.fe.specs.guihelper.gui.BasePanels.GuiTab#exitTab(pt.up.fe.specs.guihelper.Gui.BasePanels.TabData) + /** + * Called when exiting the tab. Updates the configuration file and current folder path in the DataStore. */ @Override public void exitTab() { - // Config file String path = getFilenameTextField().getEditor().getItem().toString(); if (path.trim().isEmpty()) { getData().remove(AppKeys.CONFIG_FILE); @@ -377,20 +385,19 @@ public void exitTab() { File configFile = new File(path); configFile = configFile.getAbsoluteFile(); - // For the case when there is no config file defined File workingFolder = configFile; if (!configFile.isDirectory()) { workingFolder = configFile.getParentFile(); } - // String workingFolderPath = configFile.getAbsoluteFile().getParent(); getData().set(AppKeys.CONFIG_FILE, new File(getFilenameTextField().getEditor().getItem().toString())); getData().set(JOptionKeys.CURRENT_FOLDER_PATH, Optional.of(workingFolder.getPath())); - // getData().set(JOptionKeys.CURRENT_FOLDER_PATH, workingFolder.getPath()); } - /* (non-Javadoc) - * @see pt.up.fe.specs.guihelper.gui.BasePanels.GuiTab#getTabName() + /** + * Gets the name of the tab. + * + * @return the tab name */ @Override public String getTabName() { diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/TabProvider.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/TabProvider.java index 63d17390..6531c588 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/TabProvider.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/TabProvider.java @@ -15,7 +15,17 @@ import org.suikasoft.jOptions.Interfaces.DataStore; +/** + * Provider interface for creating additional tabs in the application GUI. + * + *

This interface defines a contract for providing custom tabs to the main application window. + */ public interface TabProvider { - + /** + * Returns a custom tab for the application, given a DataStore. + * + * @param data the DataStore + * @return a GuiTab instance + */ GuiTab getTab(DataStore data); } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/TabbedPane.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/TabbedPane.java index d20099ea..6d503481 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/app/TabbedPane.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/app/TabbedPane.java @@ -30,6 +30,8 @@ /** * Panel which contains the principal panels of the program and coordinates updates between panels. + * + *

This panel manages the main tabs of the application, including program and options panels. * * @author Joao Bispo */ @@ -39,6 +41,11 @@ public class TabbedPane extends JPanel { private static final DataKey APP_NAME = KeyFactory.string("tabbed pane app name"); + /** + * Returns the DataKey for the application name. + * + * @return the DataKey for the app name + */ public static DataKey getAppNameKey() { return APP_NAME; } @@ -49,6 +56,11 @@ public static DataKey getAppNameKey() { private final OptionsPanel optionsPanel; + /** + * Constructs a TabbedPane for the given application. + * + * @param application the application to display + */ public TabbedPane(App application) { super(new GridLayout(1, 1)); @@ -75,16 +87,6 @@ public TabbedPane(App application) { tabs.add(provider.getTab(tabData)); } - // Check if program uses global options - /* - if (AppUsesGlobalOptions.class.isInstance(application)) { - GlobalOptionsPanel globalPanel = new GlobalOptionsPanel( - ((AppUsesGlobalOptions) application).getGlobalOptions()); - - tabs.add(globalPanel); - } - */ - int baseMnemonic = KeyEvent.VK_1; int currentIndex = 0; for (GuiTab tab : tabs) { @@ -125,6 +127,11 @@ public void stateChanged(ChangeEvent evt) { } + /** + * Returns the options panel associated with this TabbedPane. + * + * @return the options panel + */ public OptionsPanel getOptionsPanel() { return optionsPanel; } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/BooleanPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/BooleanPanel.java index 64762950..0d64e3a5 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/BooleanPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/BooleanPanel.java @@ -8,7 +8,7 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.gui.panels.option; @@ -24,8 +24,9 @@ import pt.up.fe.specs.util.SpecsSwing; /** + * Panel for editing boolean values using a JCheckBox. * - * @author Joao Bispo + *

This panel provides a checkbox for boolean DataKey values in the GUI. */ public class BooleanPanel extends KeyPanel { @@ -36,6 +37,13 @@ public class BooleanPanel extends KeyPanel { */ private final JCheckBox checkBox; + /** + * Constructs a BooleanPanel for the given DataKey, DataStore, and label. + * + * @param key the DataKey + * @param data the DataStore + * @param label the label for the checkbox + */ public BooleanPanel(DataKey key, DataStore data, String label) { super(key, data); @@ -45,19 +53,40 @@ public BooleanPanel(DataKey key, DataStore data, String label) { add(checkBox, BorderLayout.CENTER); } + /** + * Constructs a BooleanPanel for the given DataKey and DataStore. + * + * @param key the DataKey + * @param data the DataStore + */ public BooleanPanel(DataKey key, DataStore data) { this(key, data, key.getName()); } + /** + * Returns the JCheckBox component. + * + * @return the JCheckBox + */ public JCheckBox getCheckBox() { return checkBox; } + /** + * Returns the current boolean value of the checkbox. + * + * @return true if selected, false otherwise + */ @Override public Boolean getValue() { return checkBox.isSelected(); } + /** + * Sets the value of the checkbox. + * + * @param value the boolean value to set + */ @Override public void setValue(Boolean value) { SpecsSwing.runOnSwing(() -> checkBox.setSelected(value)); diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/DoublePanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/DoublePanel.java index e92e1fb1..4037fa9a 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/DoublePanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/DoublePanel.java @@ -24,8 +24,9 @@ import pt.up.fe.specs.util.SpecsStrings; /** + * Panel for editing double values using a JTextField. * - * @author Joao Bispo + *

This panel provides a text field for double DataKey values in the GUI. */ public class DoublePanel extends KeyPanel { @@ -36,6 +37,12 @@ public class DoublePanel extends KeyPanel { */ private final JTextField value; + /** + * Constructs a DoublePanel for the given DataKey and DataStore. + * + * @param key the DataKey + * @param data the DataStore + */ public DoublePanel(DataKey key, DataStore data) { super(key, data); @@ -45,14 +52,29 @@ public DoublePanel(DataKey key, DataStore data) { setLayout(new FlowLayout(FlowLayout.LEFT)); } + /** + * Sets the text of the text field. + * + * @param text the text to set + */ private void setText(String text) { value.setText(text); } + /** + * Gets the text from the text field. + * + * @return the text in the field + */ private String getText() { return value.getText(); } + /** + * Returns the current double value from the text field. + * + * @return the double value + */ @Override public Double getValue() { String stringValue = getText(); @@ -65,6 +87,11 @@ public Double getValue() { return SpecsStrings.decodeDouble(stringValue, () -> getKey().getDefault().orElse(0.0)); } + /** + * Sets the value of the text field. + * + * @param value the double value to set + */ @Override public void setValue(Double value) { setText(value.toString()); diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/EnumMultipleChoicePanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/EnumMultipleChoicePanel.java index 2e2f92a8..c0828f8e 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/EnumMultipleChoicePanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/EnumMultipleChoicePanel.java @@ -29,8 +29,11 @@ import pt.up.fe.specs.util.SpecsSwing; /** - * - * @author Joao Bispo + * Panel for selecting enum values from a combo box. + * + *

This panel provides a combo box for selecting enum DataKey values in the GUI. + * + * @param the enum type */ public class EnumMultipleChoicePanel> extends KeyPanel { @@ -39,14 +42,18 @@ public class EnumMultipleChoicePanel> extends KeyPanel { /** * INSTANCE VARIABLES */ - // private final JComboBox comboBoxValues; private final JComboBox comboBoxValues; private final Collection availableChoices; + /** + * Constructs an EnumMultipleChoicePanel for the given DataKey and DataStore. + * + * @param key the DataKey + * @param data the DataStore + */ public EnumMultipleChoicePanel(DataKey key, DataStore data) { super(key, data); - // comboBoxValues = new JComboBox(); comboBoxValues = new JComboBox<>(); T[] enumConstants = key.getValueClass().getEnumConstants(); @@ -54,11 +61,7 @@ public EnumMultipleChoicePanel(DataKey key, DataStore data) { availableChoices = new HashSet<>(Arrays.asList(enumConstants)); for (T choice : enumConstants) { - // comboBoxValues.addItem(choice); comboBoxValues.addItem(valueToString(choice)); - // key.getDecoder() - // .map(codec -> codec.encode(choice)) - // .orElse(choice.name())); } // Check if there is a default value @@ -66,29 +69,36 @@ public EnumMultipleChoicePanel(DataKey key, DataStore data) { .map(defaultValue -> valueToString(defaultValue)) .ifPresent(comboBoxValues::setSelectedItem); - // if (getKey().getDefault().isPresent()) { - // comboBoxValues.setSelectedItem(getKey().getDefault().get()); - // } - setLayout(new BorderLayout()); add(comboBoxValues, BorderLayout.CENTER); } + /** + * Converts an enum value to its string representation using the key's decoder if present. + * + * @param value the enum value + * @return the string representation + */ private String valueToString(T value) { return getKey().getDecoder() .map(codec -> codec.encode(value)) .orElse(value.name()); } - /* - private JComboBox getValues() { - return comboBoxValues; - } - */ + /** + * Returns the combo box component for selecting values. + * + * @return the combo box + */ private JComboBox getValues() { return comboBoxValues; } + /** + * Returns the currently selected enum value. + * + * @return the selected enum value + */ @Override public T getValue() { var stringValue = getValues().getItemAt(getValues().getSelectedIndex()); @@ -96,9 +106,14 @@ public T getValue() { return getKey().getDecoder() .map(codec -> codec.decode(stringValue)) .orElseGet(() -> SpecsEnums.valueOf(getKey().getValueClass(), stringValue)); - } + /** + * Sets the selected value in the combo box. + * + * @param value the value to set + * @param the enum type + */ @Override public void setValue(ET value) { // Choose first if value is null diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/FilePanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/FilePanel.java index f06bc80a..bbe1304d 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/FilePanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/FilePanel.java @@ -36,8 +36,9 @@ import pt.up.fe.specs.util.SpecsIo; /** + * Panel for selecting and displaying file or directory paths using a text field and browse button. * - * @author Joao Bispo + *

This panel provides a file chooser dialog and text field for DataKey values of type File. */ public class FilePanel extends KeyPanel { @@ -54,20 +55,21 @@ public class FilePanel extends KeyPanel { /** * Helper constructor for a FilePanel that has a browse button for files and folders. - * - * @param key - * @param data + * + * @param key the DataKey + * @param data the DataStore */ public FilePanel(DataKey key, DataStore data) { this(key, data, JFileChooser.FILES_AND_DIRECTORIES, Collections.emptyList()); } /** - * - * @param key - * @param data - * @param fileChooserMode - * JFileChooser option + * Constructs a FilePanel with a specific file chooser mode and file extensions. + * + * @param key the DataKey + * @param data the DataStore + * @param fileChooserMode JFileChooser option + * @param extensions the allowed file extensions */ public FilePanel(DataKey key, DataStore data, int fileChooserMode, Collection extensions) { super(key, data); @@ -99,14 +101,21 @@ public FilePanel(DataKey key, DataStore data, int fileChooserMode, Collect } + /** + * Sets the text in the text field. + * + * @param text the text to set + */ public void setText(String text) { - // Normalize text - // text = SpecsIo.normalizePath(text); textField.setText(text); } + /** + * Gets the text from the text field. + * + * @return the text in the text field + */ public String getText() { - // return SpecsIo.normalizePath(textField.getText()); return textField.getText(); } @@ -148,7 +157,6 @@ private static File getFile(String fieldValue, DataKey key, DataStore data Optional currentFolderPath = data.get(JOptionKeys.CURRENT_FOLDER_PATH); if (!currentFolderPath.isPresent()) { - // LoggingUtils.msgWarn("CHECK THIS CASE, WHEN CONFIG IS NOT DEFINED"); return new File(fieldValue); } @@ -167,6 +175,11 @@ private static File getFile(String fieldValue, DataKey key, DataStore data } + /** + * Gets the value of the file from the text field. + * + * @return the file value + */ @Override public File getValue() { return getFile(textField.getText(), getKey(), getData()); @@ -183,14 +196,11 @@ private static String processFile(DataStore data, File file) { // When showing the path in the GUI, make it relative to the current setup file Optional currentFolder = data.get(JOptionKeys.CURRENT_FOLDER_PATH); - // System.out.println("GUI SET ENTRY VALUE:" + currentValue); - // System.out.println("GUI SET CURRENT FOLDER:" + currentFolder); if (currentFolder.isPresent()) { String relativePath = SpecsIo.getRelativePath(currentValue, new File(currentFolder.get())); currentValue = new File(relativePath); } - // System.out.println("CURRENT FOLDER:" + currentFolder); // If path is absolute, make it canonical if (currentValue.isAbsolute()) { return SpecsIo.getCanonicalFile(currentValue).getPath(); @@ -199,53 +209,42 @@ private static String processFile(DataStore data, File file) { return currentValue.getPath(); } + /** + * Sets the value of the file in the text field. + * + * @param value the file value to set + */ @Override public void setValue(ET value) { setText(processFile(getData(), value)); - /* - // If empty path, set empty text field - if (value.getPath().isEmpty()) { - setText(""); - return; - } - - File currentValue = value; - - // When showing the path in the GUI, make it relative to the current setup file - - Optional currentFolder = getData().getTry(JOptionKeys.CURRENT_FOLDER_PATH); - // System.out.println("GUI SET ENTRY VALUE:" + currentValue); - // System.out.println("GUI SET CURRENT FOLDER:" + currentFolder); - if (currentFolder.isPresent()) { - String relativePath = IoUtils.getRelativePath(currentValue, new File(currentFolder.get())); - currentValue = new File(relativePath); - } - - // System.out.println("CURRENT FOLDER:" + currentFolder); - // If path is absolute, make it canonical - if (currentValue.isAbsolute()) { - setText(IoUtils.getCanonicalFile(currentValue).getPath()); - } else { - setText(currentValue.getPath()); - } - - // System.out.println("GUI SET VALUE:" + currentValue); - */ } /** - * Event when opening a file - * - * @param f + * Event when opening a file. + * + * @param f the file being opened */ public void openFile(File f) { } + /** + * Sets the action to be performed when a file is opened. + * + * @param opener the FileOpener instance + */ public void setOnFileOpened(FileOpener opener) { fileOpener = opener; } + /** + * Interface for handling file opening events. + */ public interface FileOpener { + /** + * Called when a file is opened. + * + * @param f the file that was opened + */ public void onOpenFile(File f); } } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/FilesWithBaseFoldersPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/FilesWithBaseFoldersPanel.java index a14cf631..55c747e7 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/FilesWithBaseFoldersPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/FilesWithBaseFoldersPanel.java @@ -24,8 +24,9 @@ import org.suikasoft.jOptions.gui.KeyPanel; /** + * Panel for editing mappings of files to base folders using a text field. * - * @author Joao Bispo + *

This panel provides a text field for DataKey values of type Map in the GUI. */ public class FilesWithBaseFoldersPanel extends KeyPanel> { @@ -36,6 +37,12 @@ public class FilesWithBaseFoldersPanel extends KeyPanel> { */ private final JTextField textField; + /** + * Constructs a FilesWithBaseFoldersPanel for the given DataKey and DataStore. + * + * @param key the DataKey + * @param data the DataStore + */ public FilesWithBaseFoldersPanel(DataKey> key, DataStore data) { super(key, data); @@ -46,26 +53,43 @@ public FilesWithBaseFoldersPanel(DataKey> key, DataStore data) { } + /** + * Sets the text of the text field. + * + * @param text the text to set + */ public void setText(String text) { textField.setText(text); } + /** + * Gets the text from the text field. + * + * @return the text in the field + */ public String getText() { return textField.getText(); } + /** + * Returns the current value as a map of files to base folders. + * + * @return the map value + */ @Override public Map getValue() { return getKey().decode(getText()); } + /** + * Sets the value of the text field from a map. + * + * @param value the map value to set + * @param the type of value (extends Map) + */ @Override public > void setValue(ET value) { - // System.out.println("DATA: " + getData()); - // Simplify value before setting - // System.out.println("ORIGINAL VALUE: " + value); var simplifiedValue = getKey().getCustomSetter().get().get(value, getData()); - // System.out.println("SIMPLIFIED VALUE:\n" + simplifiedValue); setText(getKey().encode(simplifiedValue)); } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/GenericStringPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/GenericStringPanel.java index bfbb0b8d..861a99f2 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/GenericStringPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/GenericStringPanel.java @@ -22,8 +22,11 @@ import org.suikasoft.jOptions.gui.KeyPanel; /** + * Abstract panel for editing string-based values using a JTextField. * - * @author Joao Bispo + *

This panel provides a text field for DataKey values of type T, to be extended for specific types. + * + * @param the type of value handled by the panel */ public abstract class GenericStringPanel extends KeyPanel { @@ -34,21 +37,37 @@ public abstract class GenericStringPanel extends KeyPanel { */ private final JTextField textField; + /** + * Constructs a GenericStringPanel for the given DataKey and DataStore. + * + * @param key the DataKey + * @param data the DataStore + */ public GenericStringPanel(DataKey key, DataStore data) { - super(key, data); + super(key, data); - textField = new JTextField(); + textField = new JTextField(); - setLayout(new BorderLayout()); - add(textField, BorderLayout.CENTER); + setLayout(new BorderLayout()); + add(textField, BorderLayout.CENTER); } + /** + * Sets the text of the text field. + * + * @param text the text to set + */ public void setText(String text) { - textField.setText(text); + textField.setText(text); } + /** + * Gets the text from the text field. + * + * @return the text in the field + */ public String getText() { - return textField.getText(); + return textField.getText(); } } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/IntegerPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/IntegerPanel.java index 5ab0636f..745660dc 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/IntegerPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/IntegerPanel.java @@ -25,8 +25,9 @@ import pt.up.fe.specs.util.SpecsStrings; /** + * Panel for editing integer values using a JTextField. * - * @author Joao Bispo + *

This panel provides a text field for integer DataKey values in the GUI. */ public class IntegerPanel extends KeyPanel { @@ -37,6 +38,12 @@ public class IntegerPanel extends KeyPanel { */ private final JTextField value; + /** + * Constructs an IntegerPanel for the given DataKey and DataStore. + * + * @param key the DataKey + * @param data the DataStore + */ public IntegerPanel(DataKey key, DataStore data) { super(key, data); @@ -47,14 +54,29 @@ public IntegerPanel(DataKey key, DataStore data) { setLayout(new FlowLayout(FlowLayout.LEFT)); } + /** + * Sets the text of the text field. + * + * @param text the text to set + */ private void setText(String text) { value.setText(text); } + /** + * Gets the text from the text field. + * + * @return the text in the field + */ private String getText() { return value.getText(); } + /** + * Returns the current integer value from the text field. + * + * @return the integer value + */ @Override public Integer getValue() { String stringValue = getText(); @@ -73,6 +95,11 @@ public Integer getValue() { return result; } + /** + * Sets the value of the text field. + * + * @param value the integer value to set + */ @Override public void setValue(Integer value) { setText(value.toString()); diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/MultiEnumMultipleChoicePanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/MultiEnumMultipleChoicePanel.java index a22b0966..73725deb 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/MultiEnumMultipleChoicePanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/MultiEnumMultipleChoicePanel.java @@ -28,8 +28,11 @@ import pt.up.fe.specs.util.SpecsCheck; /** - * - * @author Joao Bispo + * Panel for selecting multiple enum values using combo boxes. + * + *

This panel provides controls for selecting and managing multiple enum values for a DataKey of type List. + * + * @param the enum type */ public class MultiEnumMultipleChoicePanel> extends KeyPanel> { @@ -43,8 +46,12 @@ public class MultiEnumMultipleChoicePanel> extends KeyPanel

  • availableChoices; - + /** + * Constructs a MultiEnumMultipleChoicePanel for the given DataKey and DataStore. + * + * @param key the DataKey + * @param data the DataStore + */ public MultiEnumMultipleChoicePanel(DataKey> key, DataStore data) { super(key, data); @@ -52,37 +59,22 @@ public MultiEnumMultipleChoicePanel(DataKey> key, DataStore data) { removeButton = new JButton("X"); selectedElements = new JComboBox<>(); - - // comboBoxValues = new JComboBox(); availableElements = new JComboBox<>(); // Add actions addButton.addActionListener(this::addButtonAction); removeButton.addActionListener(this::removeButtonAction); - // Default must be defined, otherwise we are not able to populate the avaliable choices + // Default must be defined, otherwise we are not able to populate the available choices var defaultValues = key.getDefault().orElseThrow( () -> new RuntimeException("Must define a default value, otherwise we cannot obtain Enum class")); SpecsCheck.checkArgument(!defaultValues.isEmpty(), () -> "Default value must not be empty, otherwise we cannot obtain Enum class"); - // @SuppressWarnings("unchecked") - // T[] enumConstants = ((Class) defaultEnum.getClass()).getEnumConstants(); - - // availableChoices = new HashSet<>(Arrays.asList(enumConstants)); - for (T choice : defaultValues) { availableElements.addItem(choice); } - // Check if there is a default value - // if (getKey().getDefault().isPresent()) { - // for (var defaultElement : getKey().getDefault().get()) { - // System.out.println("ADDING DEFAULT: " + defaultElement); - // addElement(defaultElement); - // } - // } - setLayout(new FlowLayout(FlowLayout.LEFT)); add(selectedElements); add(availableElements); @@ -90,6 +82,12 @@ public MultiEnumMultipleChoicePanel(DataKey> key, DataStore data) { add(removeButton); } + /** + * Returns the elements in the given combo box as a list. + * + * @param comboBox the combo box + * @return the list of elements + */ private List getElements(JComboBox comboBox) { List elements = new ArrayList<>(); for (int i = 0; i < comboBox.getItemCount(); i++) { @@ -98,6 +96,13 @@ private List getElements(JComboBox comboBox) { return elements; } + /** + * Returns the index of the given element in the combo box. + * + * @param comboBox the combo box + * @param element the element to find + * @return the index of the element, or -1 if not found + */ private int indexOf(JComboBox comboBox, T element) { for (int i = 0; i < comboBox.getItemCount(); i++) { if (element.equals(comboBox.getItemAt(i))) { @@ -107,29 +112,29 @@ private int indexOf(JComboBox comboBox, T element) { return -1; } + /** + * Moves an element from the source combo box to the destination combo box. + * + * @param element the element to move + * @param source the source combo box + * @param destination the destination combo box + */ private void moveElement(T element, JComboBox source, JComboBox destination) { - // Check if element is present is available choices - // var available = getElements(availableElements); - // int elementIndex = available.indexOf(element); - int elementIndex = indexOf(source, element); if (elementIndex == -1) { - // SpecsLogs.warn("Could not find element: " + element); return; } destination.addItem(element); source.removeItemAt(elementIndex); - // availableElements.getIt } /** - * Adds the option from the avaliable list to selected list. - * - * @param evt + * Adds the option from the available list to the selected list. + * + * @param evt the action event */ private void addButtonAction(ActionEvent evt) { - // Determine what element is selected int choice = availableElements.getSelectedIndex(); if (choice == -1) { return; @@ -140,11 +145,10 @@ private void addButtonAction(ActionEvent evt) { /** * Removes the option from the selected list to the available list. - * - * @param evt + * + * @param evt the action event */ private void removeButtonAction(ActionEvent evt) { - // Determine what element is selected int choice = selectedElements.getSelectedIndex(); if (choice == -1) { return; @@ -153,39 +157,26 @@ private void removeButtonAction(ActionEvent evt) { moveElement(selectedElements.getItemAt(choice), selectedElements, availableElements); } - // private JComboBox getValues() { - // return availableElements; - // } - + /** + * Returns the current value of the selected elements. + * + * @return the list of selected elements + */ @Override public List getValue() { return getElements(selectedElements); } + /** + * Sets the value of the selected elements. + * + * @param value the list of elements to set + * @param the type of the list + */ @Override public > void setValue(ET value) { for (var element : value) { moveElement(element, availableElements, selectedElements); } - - /* - // Choose first if value is null - T currentValue = value; - - if (currentValue == null) { - currentValue = getKey().getValueClass().getEnumConstants()[0]; - } - - if (!availableChoices.contains(currentValue)) { - SpecsLogs.warn( - "Could not find choice '" + currentValue + "'. Available " + "choices: " + availableChoices); - currentValue = getKey().getValueClass().getEnumConstants()[0]; - } - - T finalValue = currentValue; - - SpecsSwing.runOnSwing(() -> comboBoxValues.setSelectedItem(finalValue)); - */ } - } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/MultipleChoiceListPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/MultipleChoiceListPanel.java index a9dfda84..a5675240 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/MultipleChoiceListPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/MultipleChoiceListPanel.java @@ -27,9 +27,15 @@ import org.suikasoft.jOptions.gui.KeyPanel; /** + * Panel for selecting multiple values from a list of choices. + * + *

    + * This panel provides controls for adding, removing, and managing multiple + * choices for a DataKey of type List. + * * TODO: Keep order as given by the original elements. * - * @author Joao Bispo + * @param the type of value handled by the panel */ public class MultipleChoiceListPanel extends KeyPanel> { @@ -45,8 +51,12 @@ public class MultipleChoiceListPanel extends KeyPanel> { private final JButton addAllButton; private final JButton removeAllButton; - // private final Collection availableChoices; - + /** + * Constructs a MultipleChoiceListPanel for the given DataKey and DataStore. + * + * @param key the DataKey + * @param data the DataStore + */ public MultipleChoiceListPanel(DataKey> key, DataStore data) { super(key, data); @@ -56,8 +66,6 @@ public MultipleChoiceListPanel(DataKey> key, DataStore data) { removeAllButton = new JButton("X All"); selectedElements = new JComboBox<>(); - - // comboBoxValues = new JComboBox(); availableElements = new JComboBox<>(); // Add actions @@ -66,36 +74,17 @@ public MultipleChoiceListPanel(DataKey> key, DataStore data) { addAllButton.addActionListener(this::addAllButtonAction); removeAllButton.addActionListener(this::removeAllButtonAction); - // ExtraData must be defined, otherwise we are not able to populate the avaliable choices + // ExtraData must be defined, otherwise we are not able to populate the available choices var extraData = key.getExtraData() .orElseThrow(() -> new RuntimeException("Key '" + key.getName() + "' must define extra data")); @SuppressWarnings("unchecked") List defaultValues = extraData.get(MultipleChoiceListKey.AVAILABLE_CHOICES); - // var defaultValues = key.getAvailableChoices(); - // var defaultValues = key.getDefault().orElseThrow( - // () -> new RuntimeException("Must define a default value, otherwise we cannot obtain Enum class")); - // SpecsCheck.checkArgument(!defaultValues.isEmpty(), - // () -> "Default value must not be empty, otherwise we cannot obtain Enum class"); - - // @SuppressWarnings("unchecked") - // T[] enumConstants = ((Class) defaultEnum.getClass()).getEnumConstants(); - - // availableChoices = new HashSet<>(Arrays.asList(enumConstants)); - for (T choice : defaultValues) { availableElements.addItem(choice); } - // Check if there is a default value - // if (getKey().getDefault().isPresent()) { - // for (var defaultElement : getKey().getDefault().get()) { - // System.out.println("ADDING DEFAULT: " + defaultElement); - // addElement(defaultElement); - // } - // } - setLayout(new FlowLayout(FlowLayout.LEFT)); add(selectedElements); add(availableElements); @@ -105,6 +94,12 @@ public MultipleChoiceListPanel(DataKey> key, DataStore data) { add(removeAllButton); } + /** + * Retrieves all elements from the given JComboBox. + * + * @param comboBox the JComboBox to retrieve elements from + * @return a list of elements in the JComboBox + */ private List getElements(JComboBox comboBox) { List elements = new ArrayList<>(); for (int i = 0; i < comboBox.getItemCount(); i++) { @@ -113,6 +108,13 @@ private List getElements(JComboBox comboBox) { return elements; } + /** + * Finds the index of the given element in the JComboBox. + * + * @param comboBox the JComboBox to search + * @param element the element to find + * @return the index of the element, or -1 if not found + */ private int indexOf(JComboBox comboBox, T element) { for (int i = 0; i < comboBox.getItemCount(); i++) { if (element.equals(comboBox.getItemAt(i))) { @@ -122,29 +124,29 @@ private int indexOf(JComboBox comboBox, T element) { return -1; } + /** + * Moves an element from the source JComboBox to the destination JComboBox. + * + * @param element the element to move + * @param source the source JComboBox + * @param destination the destination JComboBox + */ private void moveElement(T element, JComboBox source, JComboBox destination) { - // Check if element is present is available choices - // var available = getElements(availableElements); - // int elementIndex = available.indexOf(element); - int elementIndex = indexOf(source, element); if (elementIndex == -1) { - // SpecsLogs.warn("Could not find element: " + element); return; } destination.addItem(element); source.removeItemAt(elementIndex); - // availableElements.getIt } /** - * Adds the option from the avaliable list to selected list. - * - * @param evt + * Adds the selected option from the available list to the selected list. + * + * @param evt the ActionEvent triggered by the button */ private void addButtonAction(ActionEvent evt) { - // Determine what element is selected int choice = availableElements.getSelectedIndex(); if (choice == -1) { return; @@ -154,12 +156,11 @@ private void addButtonAction(ActionEvent evt) { } /** - * Removes the option from the selected list to the available list. - * - * @param evt + * Removes the selected option from the selected list to the available list. + * + * @param evt the ActionEvent triggered by the button */ private void removeButtonAction(ActionEvent evt) { - // Determine what element is selected int choice = selectedElements.getSelectedIndex(); if (choice == -1) { return; @@ -169,9 +170,9 @@ private void removeButtonAction(ActionEvent evt) { } /** - * Moves all options from the avaliable list to selected list. - * - * @param evt + * Moves all options from the available list to the selected list. + * + * @param evt the ActionEvent triggered by the button */ private void addAllButtonAction(ActionEvent evt) { while (availableElements.getItemCount() > 0) { @@ -181,8 +182,8 @@ private void addAllButtonAction(ActionEvent evt) { /** * Moves all options from the selected list to the available list. - * - * @param evt + * + * @param evt the ActionEvent triggered by the button */ private void removeAllButtonAction(ActionEvent evt) { while (selectedElements.getItemCount() > 0) { @@ -190,11 +191,21 @@ private void removeAllButtonAction(ActionEvent evt) { } } + /** + * Retrieves the current value of the panel. + * + * @return a list of selected elements + */ @Override public List getValue() { return getElements(selectedElements); } + /** + * Sets the value of the panel. + * + * @param value the list of elements to set as selected + */ @Override public > void setValue(ET value) { for (var element : value) { diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/SetupListPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/SetupListPanel.java index 8078a774..131ca96c 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/SetupListPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/SetupListPanel.java @@ -34,6 +34,11 @@ import pt.up.fe.specs.util.SpecsLogs; +/** + * Panel for editing and managing lists of SetupList values. + * + *

    This panel provides controls for adding, removing, and selecting setup elements for a DataKey of type SetupList. + */ public class SetupListPanel extends KeyPanel { private static final long serialVersionUID = 1L; @@ -55,9 +60,15 @@ public class SetupListPanel extends KeyPanel { private JPanel currentOptionsPanel; private List elementsBoxShadow; - // private List elementsFiles; private List elementsOptionPanels; + /** + * Constructs a SetupListPanel for the given DataKey, DataStore, and collection of StoreDefinitions. + * + * @param key the DataKey + * @param data the DataStore + * @param definitions the collection of StoreDefinitions + */ public SetupListPanel(DataKey key, DataStore data, Collection definitions) { super(key, data); @@ -72,7 +83,6 @@ public SetupListPanel(DataKey key, DataStore data, Collection(); elementsBox = new JComboBox<>(); - // elementsFiles = new ArrayList<>(); elementsOptionPanels = new ArrayList<>(); initChoices(); @@ -95,6 +105,9 @@ public SetupListPanel(DataKey key, DataStore data, Collection void setValue(ET value) { - // System.out.println("SET VALUE:" + value); // Clear previous values clearElements(); @@ -322,14 +347,13 @@ public void setValue(ET value) { } /** - * TODO: This kind of logic should be part of SetupList + * Converts a setup name to its original enum representation. * - * @param setupName - * @return + * @param setupName the setup name + * @return the original enum name */ public static String toOriginalEnum(String setupName) { - // Remove var dashIndex = setupName.indexOf("-"); if (dashIndex == -1) { return setupName; @@ -341,20 +365,13 @@ public static String toOriginalEnum(String setupName) { /** * Loads a single DataStore. * - * @param aClass - * @param aFile + * @param table the DataStore to load */ private void loadElement(DataStore table) { // Build name var enumName = toOriginalEnum(table.getName()); - // // Remove - // var dashIndex = enumName.indexOf("-"); - // if (dashIndex != -1) { - // enumName = enumName.substring(dashIndex + 1).strip(); - // } - int setupIndex = choicesBoxShadow.indexOf(enumName); if (setupIndex == -1) { @@ -366,18 +383,17 @@ private void loadElement(DataStore table) { // Create element int elementsIndex = addElement(setupIndex); - // Set option file - // elementsFiles.set(elementsIndex, table); - // Load values in the file elementsOptionPanels.get(elementsIndex).setValue(table); } + /** + * Clears all elements from the panel. + */ private void clearElements() { elementsBox.removeAllItems(); elementsBoxShadow = new ArrayList<>(); - // elementsFiles = new ArrayList<>(); elementsOptionPanels = new ArrayList<>(); } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/SetupPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/SetupPanel.java index c7022241..b64b95cc 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/SetupPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/SetupPanel.java @@ -26,6 +26,9 @@ import org.suikasoft.jOptions.storedefinition.StoreDefinition; /** + * Panel for editing and displaying a DataStore using a nested BaseSetupPanel. + * + *

    This panel provides controls for loading and displaying values for a DataKey of type DataStore. * * @author Joao Bispo */ @@ -40,84 +43,70 @@ public class SetupPanel extends KeyPanel { private final BaseSetupPanel setupOptionsPanel; + /** + * Constructs a SetupPanel for the given DataKey, DataStore, and StoreDefinition. + * + * @param key the DataKey + * @param data the DataStore + * @param definition the StoreDefinition + */ public SetupPanel(DataKey key, DataStore data, StoreDefinition definition) { - super(key, data); - - // Initiallize objects - // newPanel.add(new javax.swing.JSeparator(),0); - // newPanel.add(new javax.swing.JSeparator()); - setupOptionsPanel = new BaseSetupPanel(definition, data); - - // Add actions - /* - checkBoxShow.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - showButtonActionPerformed(e); - } - - }); - * - */ - - // Build choice panel - // choicePanel = buildChoicePanel(); - - currentOptionsPanel = null; - - // setLayout(new BorderLayout(5, 5)); - // add(choicePanel, BorderLayout.PAGE_START); - LayoutManager layout = new BoxLayout(this, BoxLayout.Y_AXIS); - setLayout(layout); - // add(choicePanel); - updateSetupOptions(); + super(key, data); + + // Initialize objects + setupOptionsPanel = new BaseSetupPanel(definition, data); + + currentOptionsPanel = null; + + LayoutManager layout = new BoxLayout(this, BoxLayout.Y_AXIS); + setLayout(layout); + updateSetupOptions(); } /** * Loads the several elements from a DataStore. + * + * @param value the DataStore to load + * @param the type of DataStore */ @Override public void setValue(ET value) { - // Load values - setupOptionsPanel.loadValues(value); + // Load values + setupOptionsPanel.loadValues(value); } + /** + * Updates the setup options panel to reflect the current state. + */ private void updateSetupOptions() { - /* - boolean show = checkBoxShow.isSelected(); - - if(!show) { - remove(currentOptionsPanel); - currentOptionsPanel = null; - revalidate(); - //repaint(); - return; - } - * - */ - - if (currentOptionsPanel != null) { - remove(currentOptionsPanel); - currentOptionsPanel = null; - } - - currentOptionsPanel = setupOptionsPanel; - add(currentOptionsPanel); - currentOptionsPanel.revalidate(); - - // TODO: Is it repaint necessary here, or revalidate on panel solves it? - // repaint(); - // System.out.println("SetupPanel Repainted"); + if (currentOptionsPanel != null) { + remove(currentOptionsPanel); + currentOptionsPanel = null; + } + + currentOptionsPanel = setupOptionsPanel; + add(currentOptionsPanel); + currentOptionsPanel.revalidate(); } + /** + * Retrieves the current value of the DataStore. + * + * @return the current DataStore + */ @Override public DataStore getValue() { - return setupOptionsPanel.getData(); + return setupOptionsPanel.getData(); } + /** + * Retrieves the collection of KeyPanels contained in the setup options panel. + * + * @return a collection of KeyPanels + */ @Override public Collection> getPanels() { - return setupOptionsPanel.getPanels().values(); + return setupOptionsPanel.getPanels().values(); } } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/StringListPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/StringListPanel.java index c12a7855..a1d13656 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/StringListPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/StringListPanel.java @@ -35,6 +35,9 @@ import pt.up.fe.specs.util.utilities.StringList; /** + * Panel for editing lists of strings using a JList and text fields. + * + *

    This panel provides controls for adding, removing, and managing string values for a DataKey of type StringList. * * @author Joao Bispo */ @@ -46,8 +49,6 @@ public class StringListPanel extends KeyPanel { * INSTANCE VARIABLES */ - // private JComboBox comboBoxValues; - // private final JComboBox comboBoxValues; private final JTextField possibleValue; private final JButton removeButton; private final JButton addButton; @@ -62,11 +63,25 @@ public class StringListPanel extends KeyPanel { private List predefinedValues; boolean isPredefinedEnabled; + /** + * Creates a new StringListPanel instance for the given DataKey and DataStore. + * + * @param key the DataKey + * @param data the DataStore + * @return a new StringListPanel + */ public static StringListPanel newInstance(DataKey key, DataStore data) { return newInstance(key, data, Collections.emptyList()); - // return new StringListPanel(key, data); } + /** + * Creates a new StringListPanel instance for the given DataKey, DataStore, and predefined values. + * + * @param key the DataKey + * @param data the DataStore + * @param predefinedLabelsValues the predefined values + * @return a new StringListPanel + */ public static StringListPanel newInstance(DataKey key, DataStore data, List predefinedLabelsValues) { @@ -75,6 +90,12 @@ public static StringListPanel newInstance(DataKey key, DataStore dat return panel; } + /** + * Constructs a StringListPanel for the given DataKey and DataStore. + * + * @param key the DataKey + * @param data the DataStore + */ public StringListPanel(DataKey key, DataStore data) { super(key, data); @@ -84,7 +105,6 @@ public StringListPanel(DataKey key, DataStore data) { jListValues = new JList<>(); jListValues.setModel(values = new DefaultListModel<>()); - // jFistValues.setCellRenderer(new CellRenderer()); removeButton = new JButton("Remove"); addButton = new JButton("Add"); possibleValue = new JTextField(); @@ -127,11 +147,6 @@ public StringListPanel(DataKey key, DataStore data) { GridBagConstraints preLabelsConstrains = new GridBagConstraints(); - // Predefined list - // TODO: Are these necessary? - // c.gridwidth = 2; - // c.gridheight = 2; - preLabelsConstrains.weightx = 1; preLabelsConstrains.weighty = 0; preLabelsConstrains.gridx = 0; @@ -140,7 +155,6 @@ public StringListPanel(DataKey key, DataStore data) { predefinedList.setVisible(false); add(predefinedList, preLabelsConstrains); - // Add button GridBagConstraints addConstrains = new GridBagConstraints(); addConstrains.fill = GridBagConstraints.HORIZONTAL; @@ -151,57 +165,27 @@ public StringListPanel(DataKey key, DataStore data) { addConstrains.gridy = 3; addPredefinedButton.setVisible(false); add(addPredefinedButton, addConstrains); - - // c.gridy = 3; - // add(addPredefinedButton, c); - - /* - c.fill = GridBagConstraints.HORIZONTAL; - c.gridheight = 1; - c.weightx = 0; - c.weighty = 0; - c.gridx = 1; - c.gridy = 0; - add(predefinedList, c); - c.gridy = 1; - add(removeButton, c); - */ - // comboBoxValues = new JComboBox<>(); - // removeButton = new JButton("X"); - // // removeButton = new JButton("Remove"); - // possibleValue = new JTextField(10); - // addButton = new JButton("Add"); - - // addButton.addActionListener(evt -> addButtonActionPerformed(evt)); - // - // removeButton.addActionListener(evt -> removeButtonActionPerformed(evt)); - - // add(comboBoxValues); - // add(removeButton); - // add(possibleValue); - // add(addButton); - } + /** + * Initializes predefined values for the panel. + * + * @param labels the predefined labels + * @param values the predefined values + */ private void initPredefinedValues(List labels, List values) { if (!isPredefinedEnabled) { isPredefinedEnabled = true; - // System.out.println("SET VISIBLE"); - predefinedList.setVisible(true); addPredefinedButton.setVisible(true); - // System.out.println("SWING THREAD"); repaint(); revalidate(); - } - // Remove previous values, if present predefinedList.removeAllItems(); - // Update values this.predefinedLabels = new ArrayList<>(labels); this.predefinedValues = new ArrayList<>(values); @@ -212,14 +196,15 @@ private void initPredefinedValues(List labels, List values) { predefinedList.revalidate(); predefinedList.repaint(); }); - // - // revalidate(); - // repaint(); } + /** + * Sets predefined values for the panel. + * + * @param labelValuePairs the predefined label-value pairs + */ public void setPredefinedValues(List labelValuePairs) { - // If empty and not initialized, just return if (labelValuePairs.isEmpty() && !isPredefinedEnabled) { return; } @@ -240,13 +225,11 @@ public void setPredefinedValues(List labelValuePairs) { } /** - * Adds the text in the textfield to the combo box - * - * @param evt + * Adds the text in the textfield to the list. + * + * @param evt the action event */ private void addButtonActionPerformed(ActionEvent evt) { - // System.out.println("Current item number:"+values.getSelectedIndex()); - // Check if there is text in the textfield String newValueTrimmed = possibleValue.getText().trim(); if (newValueTrimmed.isEmpty()) { return; @@ -256,27 +239,28 @@ private void addButtonActionPerformed(ActionEvent evt) { } /** - * Adds the predefined value to the list if not present yet - * - * @param evt + * Adds the predefined value to the list if not present yet. + * + * @param evt the action event */ private void addPredefinedButtonActionPerformed(ActionEvent evt) { - // Check selected predefined value var selectedItemIndex = predefinedList.getSelectedIndex(); var value = predefinedValues.get(selectedItemIndex); - // If already contains value, do nothing if (values.contains(value)) { return; } - // Add value addValue(value); - } + /** + * Adds a new value to the list. + * + * @param newValue the new value + */ private void addValue(String newValue) { values.addElement(newValue); jListValues.setSelectedIndex(values.size() - 1); @@ -284,8 +268,8 @@ private void addValue(String newValue) { /** * Removes the currently selected element of the list. - * - * @param evt + * + * @param evt the action event */ private void removeButtonActionPerformed(ActionEvent evt) { int valueIndex = jListValues.getSelectedIndex(); @@ -303,20 +287,11 @@ private void removeButtonActionPerformed(ActionEvent evt) { } } - // public JComboBox getValues() { - /* - public JComboBox getValues() { - return comboBoxValues; - } - */ - - // public void setValues(JComboBox values) { - /* - public void setValues(JComboBox values) { - this.comboBoxValues = values; - } - */ - + /** + * Gets the current value of the panel. + * + * @return the current value + */ @Override public StringList getValue() { List newValues = new ArrayList<>(); @@ -328,6 +303,11 @@ public StringList getValue() { return StringList.newInstance(newValues.toArray(new String[0])); } + /** + * Sets the value of the panel. + * + * @param stringList the new value + */ @Override public void setValue(ET stringList) { diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/StringPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/StringPanel.java index a4aee036..4b64a04e 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/StringPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/StringPanel.java @@ -22,8 +22,9 @@ import org.suikasoft.jOptions.gui.KeyPanel; /** + * Panel for editing string values using a JTextField. * - * @author Joao Bispo + *

    This panel provides a text field for string DataKey values in the GUI. */ public class StringPanel extends KeyPanel { @@ -34,31 +35,56 @@ public class StringPanel extends KeyPanel { */ private final JTextField textField; + /** + * Constructs a StringPanel for the given DataKey and DataStore. + * + * @param key the DataKey + * @param data the DataStore + */ public StringPanel(DataKey key, DataStore data) { - super(key, data); - - textField = new JTextField(); + super(key, data); - setLayout(new BorderLayout()); - add(textField, BorderLayout.CENTER); + textField = new JTextField(); + setLayout(new BorderLayout()); + add(textField, BorderLayout.CENTER); } + /** + * Sets the text of the text field. + * + * @param text the text to set + */ public void setText(String text) { - textField.setText(text); + textField.setText(text); } + /** + * Gets the text from the text field. + * + * @return the text in the field + */ public String getText() { - return textField.getText(); + return textField.getText(); } + /** + * Returns the current string value from the text field. + * + * @return the string value + */ @Override public String getValue() { - return getText(); + return getText(); } + /** + * Sets the value of the text field. + * + * @param value the string value to set + */ @Override public void setValue(String value) { - setText(value); + setText(value); } } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/IntegratedSetupPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/IntegratedSetupPanel.java index 443cff64..f779b3a9 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/IntegratedSetupPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/IntegratedSetupPanel.java @@ -29,8 +29,9 @@ import pt.up.fe.specs.util.SpecsLogs; /** + * Panel for integrating setup options into a single panel. * - * @author Joao Bispo + *

    This panel displays setup options for a SingleSetup instance. */ public class IntegratedSetupPanel extends FieldPanel { @@ -41,75 +42,103 @@ public class IntegratedSetupPanel extends FieldPanel { */ private BaseSetupPanel setupOptionsPanel; + /** + * Constructs an IntegratedSetupPanel for the given SingleSetup. + * + * @param setup the SingleSetup instance + */ public IntegratedSetupPanel(SingleSetup setup) { - // Initiallize objects - SetupDefinition setupDefinition = setup.getSetupOptions(); - if (setupDefinition == null) { - SpecsLogs.warn("null SetupDefinition inside '" + setup.getClass() + "'"); - } else { - initChoices(setupDefinition); - } - - // initChoices(setup); - - LayoutManager layout = new BoxLayout(this, BoxLayout.Y_AXIS); - setLayout(layout); - - add(setupOptionsPanel); + // Initialize objects + SetupDefinition setupDefinition = setup.getSetupOptions(); + if (setupDefinition == null) { + SpecsLogs.warn("null SetupDefinition inside '" + setup.getClass() + "'"); + } else { + initChoices(setupDefinition); + } + + LayoutManager layout = new BoxLayout(this, BoxLayout.Y_AXIS); + setLayout(layout); + + add(setupOptionsPanel); } - // private void initChoices(SingleSetup setup) { + /** + * Initializes the setup options panel with the given SetupDefinition. + * + * @param setupDefinition the SetupDefinition + */ private void initChoices(SetupDefinition setupDefinition) { + BaseSetupPanel newPanel = new BaseSetupPanel(setupDefinition); - BaseSetupPanel newPanel = new BaseSetupPanel(setupDefinition); - // BaseSetupPanel newPanel = new BaseSetupPanel(setup.getSetupOptions()); - - // String labelName = setup.getSetupOptions().getSetupName(); - String labelName = setupDefinition.getSetupName(); - JLabel label = new JLabel("(" + labelName + ")"); - - newPanel.add(label, 0); - newPanel.add(new javax.swing.JSeparator(), 0); - newPanel.add(new javax.swing.JSeparator()); - this.setupOptionsPanel = newPanel; + String labelName = setupDefinition.getSetupName(); + JLabel label = new JLabel("(" + labelName + ")"); + newPanel.add(label, 0); + newPanel.add(new javax.swing.JSeparator(), 0); + newPanel.add(new javax.swing.JSeparator()); + this.setupOptionsPanel = newPanel; } + /** + * Returns the FieldType for this panel. + * + * @return the FieldType + */ @Override public FieldType getType() { - return FieldType.setup; + return FieldType.setup; } /** * Loads data from the raw Object in the FieldValue. - * - * @param choice + * + * @param value the value to load */ @Override public void updatePanel(Object value) { - SetupData newSetup = (SetupData) value; - loadSetup(newSetup); + SetupData newSetup = (SetupData) value; + loadSetup(newSetup); } + /** + * Loads the given SetupData into the setup options panel. + * + * @param newSetup the SetupData to load + */ private void loadSetup(SetupData newSetup) { - // Load values in the file - setupOptionsPanel.loadValues(newSetup); + // Load values in the file + setupOptionsPanel.loadValues(newSetup); } + /** + * Returns the current option as a FieldValue. + * + * @return the FieldValue + */ @Override public FieldValue getOption() { - SetupData updatedValues = setupOptionsPanel.getMapWithValues(); - return FieldValue.create(updatedValues, getType()); + SetupData updatedValues = setupOptionsPanel.getMapWithValues(); + return FieldValue.create(updatedValues, getType()); } + /** + * Returns the label for this panel. + * + * @return the JLabel, or null if no label is set + */ @Override public JLabel getLabel() { - return null; + return null; } + /** + * Returns the collection of FieldPanels contained in this panel. + * + * @return the collection of FieldPanels + */ @Override public Collection getPanels() { - return setupOptionsPanel.getPanels().values(); + return setupOptionsPanel.getPanels().values(); } } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/ListOfSetupsPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/ListOfSetupsPanel.java index 419121d1..f938bd5e 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/ListOfSetupsPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/ListOfSetupsPanel.java @@ -41,6 +41,9 @@ import pt.up.fe.specs.util.SpecsLogs; /** + * Panel for editing and managing a list of setup panels. + * + *

    This panel provides controls for adding, removing, and selecting setup elements for a MultipleSetup instance. * * @author Joao Bispo */ @@ -52,8 +55,6 @@ public class ListOfSetupsPanel extends FieldPanel { private JPanel choicePanel; private JLabel label; - // private JComboBox elementsBox; - // private JComboBox choicesBox; private JComboBox elementsBox; private JComboBox choicesBox; private JButton removeButton; @@ -69,356 +70,349 @@ public class ListOfSetupsPanel extends FieldPanel { // Properties private static final String ENUM_NAME_SEPARATOR = "-"; + /** + * Constructs a ListOfSetupsPanel for the given enum option, label, and MultipleSetup. + * + * @param enumOption the SetupFieldEnum + * @param labelName the label for the panel + * @param setup the MultipleSetup instance + */ public ListOfSetupsPanel(SetupFieldEnum enumOption, String labelName, MultipleSetup setup) { - // Initiallize objects - // id = enumOption; - // masterFile = null; - label = new JLabel(labelName + ":"); - removeButton = new JButton("X"); - addButton = new JButton("Add"); + label = new JLabel(labelName + ":"); + removeButton = new JButton("X"); + addButton = new JButton("Add"); - initChoices(setup); - initElements(); + initChoices(setup); + initElements(); - // Add actions - addButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent evt) { - addButtonActionPerformed(evt); - } - }); + // Add actions + addButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + addButtonActionPerformed(evt); + } + }); - removeButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent evt) { - removeButtonActionPerformed(evt); - } + removeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + removeButtonActionPerformed(evt); + } - }); + }); - elementsBox.addActionListener(new ActionListener() { + elementsBox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - elementComboBoxActionPerformed(e); - } + @Override + public void actionPerformed(ActionEvent e) { + elementComboBoxActionPerformed(e); + } - }); + }); - // Build choice panel - choicePanel = buildChoicePanel(); + // Build choice panel + choicePanel = buildChoicePanel(); - currentOptionsPanel = null; + currentOptionsPanel = null; - // setLayout(new BorderLayout(5, 5)); - // add(choicePanel, BorderLayout.PAGE_START); - LayoutManager layout = new BoxLayout(this, BoxLayout.Y_AXIS); - setLayout(layout); - add(choicePanel); + LayoutManager layout = new BoxLayout(this, BoxLayout.Y_AXIS); + setLayout(layout); + add(choicePanel); } + /** + * Initializes the choices available in the panel. + * + * @param setupList the MultipleSetup instance containing the setup definitions + */ private void initChoices(MultipleSetup setupList) { - // setups = new ArrayList(); - setups = setupList.getSetups(); - // System.out.println("Setups:"+setups); - // setups.addAll(setupList.getSetups()); - - // choicesBox = new JComboBox(); - choicesBox = new JComboBox<>(); - choicesBoxShadow = new ArrayList<>(); - - // for(SingleSetupEnum setup : setups) { - // for(String setupName : setups.getNameList()) { - for (SetupDefinition setup : setups.getSetupKeysList()) { - // String setupName = ((Enum)setup).name(); - String setupName = setup.getSetupName(); - choicesBox.addItem(setupName); - choicesBoxShadow.add(setupName); - } + setups = setupList.getSetups(); + + choicesBox = new JComboBox<>(); + choicesBoxShadow = new ArrayList<>(); + + for (SetupDefinition setup : setups.getSetupKeysList()) { + String setupName = setup.getSetupName(); + choicesBox.addItem(setupName); + choicesBoxShadow.add(setupName); + } } + /** + * Initializes the elements list and related components. + */ private void initElements() { - elementsBoxShadow = new ArrayList<>(); - // elementsBox = new JComboBox(); - elementsBox = new JComboBox<>(); - elementsFiles = new ArrayList<>(); - elementsOptionPanels = new ArrayList<>(); + elementsBoxShadow = new ArrayList<>(); + elementsBox = new JComboBox<>(); + elementsFiles = new ArrayList<>(); + elementsOptionPanels = new ArrayList<>(); } + /** + * Builds the choice panel containing controls for managing setup elements. + * + * @return the constructed JPanel + */ private JPanel buildChoicePanel() { - JPanel panel = new JPanel(); + JPanel panel = new JPanel(); - panel.add(label); - panel.add(elementsBox); - panel.add(removeButton); - panel.add(choicesBox); - panel.add(addButton); + panel.add(label); + panel.add(elementsBox); + panel.add(removeButton); + panel.add(choicesBox); + panel.add(addButton); - panel.setLayout(new FlowLayout(FlowLayout.LEFT)); + panel.setLayout(new FlowLayout(FlowLayout.LEFT)); - return panel; + return panel; } /** - * Adds the option from the avaliable list to selected list. + * Adds the option from the available list to the selected list. * - * @param evt + * @param evt the ActionEvent triggered by the add button */ private void addButtonActionPerformed(ActionEvent evt) { - // Determine what element is selected - int choice = choicesBox.getSelectedIndex(); - if (choice == -1) { - return; - } + int choice = choicesBox.getSelectedIndex(); + if (choice == -1) { + return; + } - addElement(choice); + addElement(choice); } /** * Removes the option from the selected list to the available list. * - * @param evt + * @param evt the ActionEvent triggered by the remove button */ private void removeButtonActionPerformed(ActionEvent evt) { - // Determine index of selected element to remove - int indexToRemove = elementsBox.getSelectedIndex(); - if (indexToRemove == -1) { - return; - } + int indexToRemove = elementsBox.getSelectedIndex(); + if (indexToRemove == -1) { + return; + } - removeElement(indexToRemove); + removeElement(indexToRemove); } /** - * Updates the options panel. + * Updates the options panel based on the selected element. * - * @param e + * @param e the ActionEvent triggered by the elements combo box */ private void elementComboBoxActionPerformed(ActionEvent e) { - updateSetupOptions(); + updateSetupOptions(); } @Override public FieldType getType() { - return FieldType.setupList; + return FieldType.setupList; } /** * Adds an element to the elements list, from the choices list. * + * @param choice the index of the choice to add * @return the index of the added element */ public int addElement(int choice) { - // Add index to elements - elementsBoxShadow.add(choice); - // Get setup options and create option file for element - SetupDefinition setupKeys = setups.getSetupKeysList().get(choice); + elementsBoxShadow.add(choice); + SetupDefinition setupKeys = setups.getSetupKeysList().get(choice); - elementsFiles.add(SetupData.create(setupKeys)); + elementsFiles.add(SetupData.create(setupKeys)); - BaseSetupPanel newPanel = new BaseSetupPanel(setupKeys, identationLevel + 1); + BaseSetupPanel newPanel = new BaseSetupPanel(setupKeys, identationLevel + 1); - if (!setupKeys.getSetupKeys().isEmpty()) { - // newPanel.add(new javax.swing.JSeparator(), 0); - // newPanel.add(new javax.swing.JSeparator()); - } + if (!setupKeys.getSetupKeys().isEmpty()) { + // newPanel.add(new javax.swing.JSeparator(), 0); + } - elementsOptionPanels.add(newPanel); + elementsOptionPanels.add(newPanel); - // Refresh - updateElementsComboBox(); + updateElementsComboBox(); - int elementIndex = elementsBoxShadow.size() - 1; - // Select last item - elementsBox.setSelectedIndex(elementIndex); - // Update vision of setup options - not needed, when we select, automatically updates - // updateSetupOptions(); + int elementIndex = elementsBoxShadow.size() - 1; + elementsBox.setSelectedIndex(elementIndex); - return elementIndex; + return elementIndex; } /** - * Loads several elements from an AppValue. + * Loads several elements from a FieldValue. * - * @param choice + * @param value the FieldValue containing the elements to load */ @Override - // public void updatePanel(FieldValue value) { public void updatePanel(Object value) { - // Clear previous values - clearElements(); + clearElements(); - ListOfSetups maps = (ListOfSetups) value; + ListOfSetups maps = (ListOfSetups) value; - for (SetupData key : maps.getMapOfSetups()) { - // String enumName = BaseUtils.decodeMapOfSetupsKey(key); - // extractEnumNameFromListName(key); - // loadElement(enumName, maps.get(key)); - loadElement(key); - } + for (SetupData key : maps.getMapOfSetups()) { + loadElement(key); + } } /** - * Loads a single element from a file + * Loads a single element from a SetupData instance. * - * @param aClass - * @param aFile + * @param table the SetupData instance to load */ - // private void loadElement(String enumName, SetupData table) { private void loadElement(SetupData table) { - // Build name - String enumName = table.getSetupName(); + String enumName = table.getSetupName(); - int setupIndex = choicesBoxShadow.indexOf(enumName); + int setupIndex = choicesBoxShadow.indexOf(enumName); - if (setupIndex == -1) { - SpecsLogs.getLogger().warning("Could not find enum '" + enumName + "'. Available enums:" + setups); - return; - } + if (setupIndex == -1) { + SpecsLogs.getLogger().warning("Could not find enum '" + enumName + "'. Available enums:" + setups); + return; + } - // Create element - int elementsIndex = addElement(setupIndex); + int elementsIndex = addElement(setupIndex); - // Set option file - elementsFiles.set(elementsIndex, table); - // Load values in the file - elementsOptionPanels.get(elementsIndex).loadValues(table); + elementsFiles.set(elementsIndex, table); + elementsOptionPanels.get(elementsIndex).loadValues(table); } + /** + * Updates the elements combo box with the current elements. + */ private void updateElementsComboBox() { - // Build list of strings to present - elementsBox.removeAllItems(); - for (int i = 0; i < elementsBoxShadow.size(); i++) { - // Get choice name - int choice = elementsBoxShadow.get(i); - // SingleSetupEnum setup = setups.get(choice); - // String setupName = setups.getNameList().get(choice); - String setupName = setups.getSetupKeysList().get(choice).getSetupName(); - - // String boxString = (i+1)+ " - "+((Enum)setup).name(); - // String boxString = createListName((i+1), ((Enum)setup).name()); - - // String boxString = BaseUtils.encodeMapOfSetupsKey(((Enum)setup).name(), i+1); - // String boxString = BaseUtils.encodeMapOfSetupsKey(setupName, i+1); - String boxString = buildSetupString(setupName, i + 1); - elementsBox.addItem(boxString); - } + elementsBox.removeAllItems(); + for (int i = 0; i < elementsBoxShadow.size(); i++) { + int choice = elementsBoxShadow.get(i); + String setupName = setups.getSetupKeysList().get(choice).getSetupName(); + + String boxString = buildSetupString(setupName, i + 1); + elementsBox.addItem(boxString); + } } + /** + * Updates the setup options panel based on the selected element. + */ private void updateSetupOptions() { - if (currentOptionsPanel != null) { - remove(currentOptionsPanel); - currentOptionsPanel = null; - } - - // Determine what item is selected in the elements combo - int index = elementsBox.getSelectedIndex(); - - if (index != -1) { - currentOptionsPanel = elementsOptionPanels.get(index); - add(currentOptionsPanel); - currentOptionsPanel.revalidate(); - } - - // TODO: Is it repaint necessary here, or revalidate on panel solves it? - repaint(); - // System.out.println("SetupPanel Repainted"); + if (currentOptionsPanel != null) { + remove(currentOptionsPanel); + currentOptionsPanel = null; + } + + int index = elementsBox.getSelectedIndex(); + + if (index != -1) { + currentOptionsPanel = elementsOptionPanels.get(index); + add(currentOptionsPanel); + currentOptionsPanel.revalidate(); + } + + repaint(); } /** * Removes an element from the elements list. * - * @return + * @param index the index of the element to remove */ public void removeElement(int index) { - // Check if the index is valid - if (elementsBox.getItemCount() <= index) { - SpecsLogs.getLogger().warning( - "Given index ('" + index + "')is too big. Elements size: " + elementsBox.getItemCount()); - return; - } - - // Remove shadow index, AppOptionFile and panel - elementsBoxShadow.remove(index); - elementsFiles.remove(index); - elementsOptionPanels.remove(index); - - // Refresh - updateElementsComboBox(); - - // Calculate new index of selected element and select it - int newIndex = calculateIndexAfterRemoval(index); - if (newIndex != -1) { - elementsBox.setSelectedIndex(newIndex); - } + if (elementsBox.getItemCount() <= index) { + SpecsLogs.getLogger().warning( + "Given index ('" + index + "')is too big. Elements size: " + elementsBox.getItemCount()); + return; + } + + elementsBoxShadow.remove(index); + elementsFiles.remove(index); + elementsOptionPanels.remove(index); + + updateElementsComboBox(); + + int newIndex = calculateIndexAfterRemoval(index); + if (newIndex != -1) { + elementsBox.setSelectedIndex(newIndex); + } } + /** + * Calculates the new index after an element is removed. + * + * @param index the index of the removed element + * @return the new index + */ private int calculateIndexAfterRemoval(int index) { - int numElements = elementsBox.getItemCount(); - - // If there are no elements, return -1 - if (numElements == 0) { - return -1; - } - - // If there are enough elements, the index is the same - if (numElements > index) { - return index; - } - - // If size is the same as index, it means that we removed the last element - // Return the index of the current last element - if (numElements == index) { - return index - 1; - } - - SpecsLogs.getLogger().warning("Invalid index '" + index + "' for list with '" + numElements + "' elements."); - return -1; + int numElements = elementsBox.getItemCount(); + + if (numElements == 0) { + return -1; + } + + if (numElements > index) { + return index; + } + + if (numElements == index) { + return index - 1; + } + + SpecsLogs.getLogger().warning("Invalid index '" + index + "' for list with '" + numElements + "' elements."); + return -1; } - // public Map getPackedValues() { + /** + * Retrieves the packed values of the panel. + * + * @return the ListOfSetups containing the packed values + */ public ListOfSetups getPackedValues() { - List listOfSetups = new ArrayList<>(); + List listOfSetups = new ArrayList<>(); - // For each selected panel, add the corresponding options table to the return list - for (int i = 0; i < elementsOptionPanels.size(); i++) { - listOfSetups.add(elementsOptionPanels.get(i).getMapWithValues()); - } + for (int i = 0; i < elementsOptionPanels.size(); i++) { + listOfSetups.add(elementsOptionPanels.get(i).getMapWithValues()); + } - return new ListOfSetups(listOfSetups); + return new ListOfSetups(listOfSetups); } + /** + * Clears all elements from the panel. + */ private void clearElements() { - elementsBox.removeAllItems(); + elementsBox.removeAllItems(); - elementsBoxShadow = new ArrayList<>(); - elementsFiles = new ArrayList<>(); - elementsOptionPanels = new ArrayList<>(); + elementsBoxShadow = new ArrayList<>(); + elementsFiles = new ArrayList<>(); + elementsOptionPanels = new ArrayList<>(); } @Override public FieldValue getOption() { - return FieldValue.create(getPackedValues(), getType()); + return FieldValue.create(getPackedValues(), getType()); } + /** + * Builds a string representation of a setup element. + * + * @param enumName the name of the enum + * @param index the index of the element + * @return the string representation + */ private static String buildSetupString(String enumName, int index) { - return index + ENUM_NAME_SEPARATOR + enumName; + return index + ENUM_NAME_SEPARATOR + enumName; } @Override public JLabel getLabel() { - return label; + return label; } @Override public Collection getPanels() { - return elementsOptionPanels.stream() - .map(setupPanel -> setupPanel.getPanels().values()) - .reduce(new ArrayList<>(), SpecsCollections::add); + return elementsOptionPanels.stream() + .map(setupPanel -> setupPanel.getPanels().values()) + .reduce(new ArrayList<>(), SpecsCollections::add); } } diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/MultipleChoiceListPanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/MultipleChoiceListPanel.java index 71b3a4fc..07406124 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/MultipleChoiceListPanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/MultipleChoiceListPanel.java @@ -32,8 +32,10 @@ import pt.up.fe.specs.util.utilities.StringList; /** - * - * @author Joao Bispo + * Deprecated panel for editing multiple choice lists. + * + *

    This panel was replaced with EnumMultipleChoicePanel. + * * @deprecated replaced with EnumMultipleChoicePanel */ @Deprecated @@ -46,8 +48,6 @@ public class MultipleChoiceListPanel extends FieldPanel { */ private final JLabel label; private final JLabel helper; - // private JComboBox selectedValues; - // private JComboBox possibleValues; private final JComboBox selectedValues; private final JComboBox possibleValues; private final JButton removeButton; @@ -58,16 +58,19 @@ public class MultipleChoiceListPanel extends FieldPanel { private final Collection originalChoices; + /** + * Constructs a MultipleChoiceListPanel for the given label and choices. + * + * @param labelName the label for the panel + * @param choices the available choices + */ public MultipleChoiceListPanel(String labelName, Collection choices) { label = new JLabel(labelName + ":"); helper = new JLabel("| Options:"); - // removeButton = new JButton("X"); removeButton = new JButton("Remove"); addButton = new JButton("Add"); originalChoices = choices; - // selectedValues = new JComboBox(); - // possibleValues = new JComboBox(); selectedValues = new JComboBox<>(); possibleValues = new JComboBox<>(); resetChoiceLists(); @@ -97,11 +100,12 @@ public void actionPerformed(ActionEvent evt) { setLayout(new FlowLayout(FlowLayout.LEFT)); } + /** + * Resets the choice lists to their original state. + */ private void resetChoiceLists() { selectedValues.removeAllItems(); possibleValues.removeAllItems(); - // selectedValues = new JComboBox(); - // possibleValues = new JComboBox(); selectedValuesShadow = new ArrayList<>(); possibleValuesShadow = new ArrayList<>(); @@ -116,8 +120,8 @@ private void resetChoiceLists() { /** * Moves one value from possibleValues to selectedValues. This method is not thread-safe. * - * @param valueName - * @return + * @param valueName the name of the value to add + * @return true if the value was successfully added, false otherwise */ private boolean addValue(String valueName) { if (valueName == null && possibleValuesShadow.isEmpty()) { @@ -142,8 +146,8 @@ private boolean addValue(String valueName) { /** * Moves one value from selectedValues to possibleValues. This method is not thread-safe. * - * @param valueName - * @return + * @param valueName the name of the value to remove + * @return true if the value was successfully removed, false otherwise */ private boolean removeValue(String valueName) { if (valueName == null && selectedValuesShadow.isEmpty()) { @@ -166,12 +170,11 @@ private boolean removeValue(String valueName) { } /** - * Adds the option from the avaliable list to selected list. + * Adds the option from the available list to the selected list. * - * @param evt + * @param evt the action event */ private void addButtonActionPerformed(ActionEvent evt) { - // Check if there is text in the textfield final String selectedValue = (String) possibleValues.getSelectedItem(); addValue(selectedValue); } @@ -179,38 +182,34 @@ private void addButtonActionPerformed(ActionEvent evt) { /** * Removes the option from the selected list to the available list. * - * @param evt + * @param evt the action event */ private void removeButtonActionPerformed(ActionEvent evt) { - // Check if there is text in the textfield final String selectedValue = (String) selectedValues.getSelectedItem(); removeValue(selectedValue); } /** - * The currently selected values. + * Gets the currently selected values. * - * @return currently selected values. + * @return an unmodifiable list of currently selected values */ public List getSelectedValues() { return Collections.unmodifiableList(selectedValuesShadow); } /** - * For each element in the value list, add it to the selected items. + * Updates the panel with the given value. * - * @param value + * @param value the value to update the panel with */ - // public void updatePanel(FieldValue value) { @Override public void updatePanel(Object value) { - // Reset current lists resetChoiceLists(); StringList values = (StringList) value; for (String valueName : values.getStringList()) { - // Check if it is not already in the selected list. if (selectedValuesShadow.contains(valueName)) { continue; } @@ -218,18 +217,32 @@ public void updatePanel(Object value) { } } + /** + * Gets the type of the field. + * + * @return the field type + */ @Override public FieldType getType() { return FieldType.multipleChoiceStringList; } + /** + * Gets the current option as a FieldValue. + * + * @return the current option + */ @Override public FieldValue getOption() { List values = getSelectedValues(); - // return FieldValue.create(values, getType()); return FieldValue.create(new StringList(values), getType()); } + /** + * Gets the label of the panel. + * + * @return the label + */ @Override public JLabel getLabel() { return label; diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/MultipleChoicePanel.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/MultipleChoicePanel.java index e4090634..e3f45383 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/MultipleChoicePanel.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/MultipleChoicePanel.java @@ -26,8 +26,9 @@ import pt.up.fe.specs.util.SpecsSwing; /** - * - * @author Joao Bispo + * Panel for selecting a single value from multiple choices. + * + *

    This panel provides a combo box for selecting one value from a set of choices. */ public class MultipleChoicePanel extends FieldPanel { @@ -37,13 +38,17 @@ public class MultipleChoicePanel extends FieldPanel { * INSTANCE VARIABLES */ private JLabel label; - // private JComboBox comboBoxValues; private JComboBox comboBoxValues; private Collection availableChoices; + /** + * Constructs a MultipleChoicePanel for the given label and choices. + * + * @param labelName the label for the panel + * @param choices the available choices + */ public MultipleChoicePanel(String labelName, Collection choices) { label = new JLabel(labelName + ":"); - // comboBoxValues = new JComboBox(); comboBoxValues = new JComboBox<>(); availableChoices = choices; @@ -57,43 +62,39 @@ public MultipleChoicePanel(String labelName, Collection choices) { setLayout(new FlowLayout(FlowLayout.LEFT)); } + /** + * Returns the combo box component for selecting values. + * + * @return the combo box + */ public JComboBox getValues() { - // public JComboBox getValues() { return comboBoxValues; } + /** + * Returns the current option as a FieldValue. + * + * @return the FieldValue + */ @Override public FieldValue getOption() { String selectedString = getValues().getItemAt(getValues().getSelectedIndex()); - // String selectedString = getValues().getSelectedItem(); return FieldValue.create(selectedString, getType()); } /** - * Selects the option in AppValue object. If the option could not be found, selects the first option. - * - * @param value - * @return true if the option is one of the available choices and could be selected, false otherwise + * Updates the panel to select the given value. + * + * @param value the value to select */ @Override public void updatePanel(Object value) { - // Check if the value in FieldValue is one of the possible choices - // final String currentChoice = BaseUtils.getString(value); - - // final String currentChoice = (String) value; String stringValue = (String) value; if (stringValue.isEmpty()) { stringValue = availableChoices.iterator().next(); } final String currentChoice = stringValue; - /* - String currentChoice = (String) value; - if(currentChoice.isEmpty()) { - currentChoice = availableChoices.iterator().next(); - } - * - */ boolean foundChoice = availableChoices.contains(currentChoice); @@ -112,11 +113,21 @@ public void run() { }); } + /** + * Returns the type of the field. + * + * @return the FieldType + */ @Override public FieldType getType() { return FieldType.multipleChoice; } + /** + * Returns the label component of the panel. + * + * @return the label + */ @Override public JLabel getLabel() { return label; diff --git a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/MultipleChoiceSetup.java b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/MultipleChoiceSetup.java index 3d6044f3..3dfd7477 100644 --- a/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/MultipleChoiceSetup.java +++ b/jOptions/src/org/suikasoft/jOptions/gui/panels/option/notimplementedyet/MultipleChoiceSetup.java @@ -40,8 +40,9 @@ import pt.up.fe.specs.util.SpecsLogs; /** - * - * @author Joao Bispo + * Panel for editing and managing multiple choice setup panels. + * + *

    This panel provides controls for adding and managing multiple setup elements for a MultipleSetup instance. */ public class MultipleChoiceSetup extends FieldPanel { @@ -54,11 +55,7 @@ public class MultipleChoiceSetup extends FieldPanel { private JPanel choicePanel; private JLabel label; - // private JComboBox elementsBox; - // private JComboBox choicesBox; private JComboBox choicesBox; - // private JButton removeButton; - // private JButton addButton; private List choicesBoxNames; private ListOfSetupDefinitions setups; @@ -67,402 +64,231 @@ public class MultipleChoiceSetup extends FieldPanel { private List elementsFiles; private List elementsOptionPanels; - // Properties - // private static final String ENUM_NAME_SEPARATOR = "-"; - + /** + * Constructs a MultipleChoiceSetup for the given enum option, label, and MultipleSetup. + * + * @param enumOption the SetupFieldEnum + * @param labelName the label for the panel + * @param setup the MultipleSetup instance + */ public MultipleChoiceSetup(SetupFieldEnum enumOption, String labelName, MultipleSetup setup) { - // Initiallize objects - label = new JLabel(labelName + ":"); - // removeButton = new JButton("X"); - // addButton = new JButton("Add"); - - initChoices(setup); - initElements(); - // Add choices - for (int i = 0; i < choicesBox.getItemCount(); i++) { - // System.out.println("Adding element "+i); - addElement(i); - } - - // Add actions - /* - addButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent evt) { - addButtonActionPerformed(evt); - } - }); - */ - /* - removeButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent evt) { - removeButtonActionPerformed(evt); - } - - }); - */ - // elementsBox.addActionListener(new ActionListener() { - choicesBox.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - choiceComboBoxActionPerformed(e); - } - - }); - - // Build choice panel - choicePanel = buildChoicePanel(); - - currentOptionsPanel = null; - - LayoutManager layout = new BoxLayout(this, BoxLayout.Y_AXIS); - setLayout(layout); - add(choicePanel); + // Initialize objects + label = new JLabel(labelName + ":"); + + initChoices(setup); + initElements(); + // Add choices + for (int i = 0; i < choicesBox.getItemCount(); i++) { + addElement(i); + } - } + // Add actions + choicesBox.addActionListener(new ActionListener() { - private void initChoices(MultipleSetup setupList) { - setups = setupList.getSetups(); + @Override + public void actionPerformed(ActionEvent e) { + choiceComboBoxActionPerformed(e); + } - // choicesBox = new JComboBox(); - choicesBox = new JComboBox<>(); - choicesBoxNames = new ArrayList<>(); + }); - for (SetupDefinition setup : setups.getSetupKeysList()) { - String setupName = setup.getSetupName(); - choicesBox.addItem(setupName); - choicesBoxNames.add(setupName); - } + // Build choice panel + choicePanel = buildChoicePanel(); - } + currentOptionsPanel = null; + + LayoutManager layout = new BoxLayout(this, BoxLayout.Y_AXIS); + setLayout(layout); + add(choicePanel); - private void initElements() { - elementsBoxShadow = new ArrayList<>(); - // elementsBox = new JComboBox(); - elementsFiles = new ArrayList<>(); - elementsOptionPanels = new ArrayList<>(); } - private JPanel buildChoicePanel() { - JPanel panel = new JPanel(); + /** + * Initializes the choices for the MultipleSetup. + * + * @param setupList the MultipleSetup instance + */ + private void initChoices(MultipleSetup setupList) { + setups = setupList.getSetups(); - panel.add(label); - // panel.add(elementsBox); - // panel.add(removeButton); - panel.add(choicesBox); - // panel.add(addButton); + choicesBox = new JComboBox<>(); + choicesBoxNames = new ArrayList<>(); - panel.setLayout(new FlowLayout(FlowLayout.LEFT)); + for (SetupDefinition setup : setups.getSetupKeysList()) { + String setupName = setup.getSetupName(); + choicesBox.addItem(setupName); + choicesBoxNames.add(setupName); + } - return panel; } /** - * Adds the option from the avaliable list to selected list. - * - * @param evt + * Initializes the elements for the setup panel. */ - /* - private void addButtonActionPerformed(ActionEvent evt) { - // Determine what element is selected - int choice = choicesBox.getSelectedIndex(); - if (choice == -1) { - return; - } + private void initElements() { + elementsBoxShadow = new ArrayList<>(); + elementsFiles = new ArrayList<>(); + elementsOptionPanels = new ArrayList<>(); + } - addElement(choice); - } - */ /** - * Removes the option from the selected list to the available list. - * - * @param evt + * Builds the choice panel for the setup. + * + * @return the constructed JPanel */ - /* - private void removeButtonActionPerformed(ActionEvent evt) { - // Determine index of selected element to remove - int indexToRemove = elementsBox.getSelectedIndex(); - if(indexToRemove == -1) { - return; - } - - removeElement(indexToRemove); + private JPanel buildChoicePanel() { + JPanel panel = new JPanel(); + + panel.add(label); + panel.add(choicesBox); + + panel.setLayout(new FlowLayout(FlowLayout.LEFT)); + + return panel; } - */ + /** - * Updates the options panel. - * - * @param e + * Updates the options panel based on the selected choice. + * + * @param e the ActionEvent triggered by the JComboBox */ private void choiceComboBoxActionPerformed(ActionEvent e) { - updateSetupOptions(); + updateSetupOptions(); } @Override public FieldType getType() { - return FieldType.setupList; + return FieldType.setupList; } /** * Adds an element to the elements list, from the choices list. - * + * + * @param choice the index of the choice to add * @return the index of the added element */ private int addElement(int choice) { - // Add index to elements - elementsBoxShadow.add(choice); - - // Get setup options and create option file for element - SetupDefinition setupKeys = setups.getSetupKeysList().get(choice); + // Add index to elements + elementsBoxShadow.add(choice); - elementsFiles.add(SetupData.create(setupKeys)); + // Get setup options and create option file for element + SetupDefinition setupKeys = setups.getSetupKeysList().get(choice); - BaseSetupPanel newPanel = new BaseSetupPanel(setupKeys); - // newPanel.add(new javax.swing.JSeparator(),0); - // newPanel.add(new javax.swing.JSeparator()); - elementsOptionPanels.add(newPanel); - // System.out.println("ElementOptionsPanel:"+elementsOptionPanels.size()); + elementsFiles.add(SetupData.create(setupKeys)); - // Refresh - // updateElementsComboBox(); + BaseSetupPanel newPanel = new BaseSetupPanel(setupKeys); + elementsOptionPanels.add(newPanel); - int elementIndex = elementsBoxShadow.size() - 1; + int elementIndex = elementsBoxShadow.size() - 1; - // Select last item - // elementsBox.setSelectedIndex(elementIndex); - // Update vision of setup options - not needed, when we select, automatically updates - // updateSetupOptions(); - - return elementIndex; + return elementIndex; } /** - * Loads several elements from an AppValue. - * - * @param choice + * Updates the panel with the given value. + * + * @param value the value to update the panel with */ @Override public void updatePanel(Object value) { - // Clear previous values - // clearElements(); - - /* - SetupData setupData = (SetupData) value; - loadSetup(setupData); - */ + ListOfSetups maps = (ListOfSetups) value; - ListOfSetups maps = (ListOfSetups) value; - - for (SetupData key : maps.getMapOfSetups()) { - // loadElement(key); - loadSetup(key); - } + for (SetupData key : maps.getMapOfSetups()) { + loadSetup(key); + } - // Show preferred setup - Integer choice = maps.getPreferredIndex(); - if (choice == null) { - choice = 0; - } + // Show preferred setup + Integer choice = maps.getPreferredIndex(); + if (choice == null) { + choice = 0; + } - choicesBox.setSelectedIndex(choice); - updateSetupOptions(); + choicesBox.setSelectedIndex(choice); + updateSetupOptions(); } /** * Loads the given setup. - * - * @param aClass - * @param aFile + * + * @param setupData the SetupData to load */ private void loadSetup(SetupData setupData) { - // Build name - String enumName = setupData.getSetupName(); - - int setupIndex = choicesBoxNames.indexOf(enumName); - - if (setupIndex == -1) { - SpecsLogs.getLogger().warning("Could not find enum '" + enumName + "'. Available enums:" + setups); - return; - } + // Build name + String enumName = setupData.getSetupName(); - // Create element - // int elementsIndex = addElement(setupIndex); + int setupIndex = choicesBoxNames.indexOf(enumName); - // Set option file - // elementsFiles.set(elementsIndex, setupData); - elementsFiles.set(setupIndex, setupData); - // Load values in the file - // elementsOptionPanels.get(elementsIndex).loadValues(setupData); - elementsOptionPanels.get(setupIndex).loadValues(setupData); + if (setupIndex == -1) { + SpecsLogs.getLogger().warning("Could not find enum '" + enumName + "'. Available enums:" + setups); + return; + } - } + elementsFiles.set(setupIndex, setupData); + elementsOptionPanels.get(setupIndex).loadValues(setupData); - /* - private void updateElementsComboBox() { - // Build list of strings to present - elementsBox.removeAllItems(); - for(int i=0; i index) { - return index; - } - - // If size is the same as index, it means that we removed the last element - // Return the index of the current last element - if(numElements == index) { - return index-1; - } - - LoggingUtils.getLogger(). - warning("Invalid index '"+index+"' for list with '"+numElements+"' elements."); - return -1; + + repaint(); } - */ + /** + * Retrieves the setups managed by this panel. + * + * @return the ListOfSetups instance + */ public ListOfSetups getSetups() { - // public SetupData getSetups() { - /* - // Get index of selected setup - int choice = choicesBox.getSelectedIndex(); - if (choice == -1) { - LoggingUtils.getLogger(). - warning("Could not get index of selected setup."); - return null; - } - - - if (elementsOptionPanels.isEmpty()) { - SetupDefinition setupDefinition = setups.getSetupKeysList().get(choice); - return SetupData.create(setupDefinition); - } - - return elementsOptionPanels.get(choice).getMapWithValues(); - */ - - List listOfSetups = new ArrayList<>(); - - // For each selected panel, add the corresponding options table to the return list - for (int i = 0; i < elementsOptionPanels.size(); i++) { - // int choicesIndex = elementsBoxShadow.get(i); + List listOfSetups = new ArrayList<>(); - listOfSetups.add(elementsOptionPanels.get(i).getMapWithValues()); - } + for (int i = 0; i < elementsOptionPanels.size(); i++) { + listOfSetups.add(elementsOptionPanels.get(i).getMapWithValues()); + } - ListOfSetups currentSetups = new ListOfSetups(listOfSetups); + ListOfSetups currentSetups = new ListOfSetups(listOfSetups); - // Get index of selected setup - int choice = choicesBox.getSelectedIndex(); - if (choice == -1) { - SpecsLogs.getLogger().warning("Could not get index of selected setup."); - return null; - } - currentSetups.setPreferredIndex(choice); + int choice = choicesBox.getSelectedIndex(); + if (choice == -1) { + SpecsLogs.getLogger().warning("Could not get index of selected setup."); + return null; + } + currentSetups.setPreferredIndex(choice); - // return new ListOfSetups(listOfSetups); - return currentSetups; + return currentSetups; } - /* - private void clearElements() { - //elementsBox.removeAllItems(); - - elementsBoxShadow = new ArrayList(); - elementsFiles = new ArrayList(); - elementsOptionPanels = new ArrayList(); - } - */ @Override public FieldValue getOption() { - return FieldValue.create(getSetups(), getType()); + return FieldValue.create(getSetups(), getType()); } - /* - private String buildSetupString(String enumName, int index) { - return index+ ENUM_NAME_SEPARATOR + enumName; - } - */ @Override public JLabel getLabel() { - return label; + return label; } @Override public Collection getPanels() { - return elementsOptionPanels.stream() - .map(setupPanel -> setupPanel.getPanels().values()) - .reduce(new ArrayList<>(), SpecsCollections::add); + return elementsOptionPanels.stream() + .map(setupPanel -> setupPanel.getPanels().values()) + .reduce(new ArrayList<>(), SpecsCollections::add); } } diff --git a/jOptions/src/org/suikasoft/jOptions/persistence/XmlPersistence.java b/jOptions/src/org/suikasoft/jOptions/persistence/XmlPersistence.java index e5038dc8..7559ee03 100644 --- a/jOptions/src/org/suikasoft/jOptions/persistence/XmlPersistence.java +++ b/jOptions/src/org/suikasoft/jOptions/persistence/XmlPersistence.java @@ -1,4 +1,4 @@ -/** +/* * Copyright 2013 SPeCS Research Group. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with @@ -39,14 +39,14 @@ import pt.up.fe.specs.util.utilities.LineStream; /** - * @author Joao Bispo + * XML-based implementation of AppPersistence for loading and saving DataStore objects. * + * @author Joao Bispo */ public class XmlPersistence implements AppPersistence { private final ObjectXml xmlMappings; private final Collection> options; - // Used to check values being loaded private final StoreDefinition definition; @@ -56,22 +56,9 @@ public class XmlPersistence implements AppPersistence { */ @Deprecated public XmlPersistence(Collection> options) { - // this.options = options; - // // definition = null; - // definition = StoreDefinition.newInstance("", options); - // xmlMappings = getObjectXml(definition); this(StoreDefinition.newInstance("DefinitionCreatedByXmlPersistence", options)); } - /** - * @param setupDefinition - */ - /* - public XmlPersistence(SetupDefinition setupDefinition) { - this(setupDefinition.getKeys()); - } - */ - /** * @deprecated Can only use constructor that receives storeDefinition */ @@ -80,27 +67,50 @@ public XmlPersistence() { this(new ArrayList<>()); } + /** + * Constructs an XmlPersistence with the given StoreDefinition. + * + * @param storeDefinition the StoreDefinition to use + */ public XmlPersistence(StoreDefinition storeDefinition) { options = storeDefinition.getKeys(); xmlMappings = getObjectXml(storeDefinition); definition = storeDefinition; } + /** + * Adds class mappings to the XML serializer. + * + * @param classes the list of classes to add + */ public void addMappings(List> classes) { xmlMappings.addMappings(classes); - } + /** + * Adds a single class mapping to the XML serializer. + * + * @param name the mapping name + * @param aClass the class to map + */ public void addMapping(String name, Class aClass) { xmlMappings.addMappings(name, aClass); } + /** + * Returns the current XML class mappings. + * + * @return a map of mapping names to classes + */ public Map> getMappings() { return xmlMappings.getMappings(); } - /* (non-Javadoc) - * @see org.suikasoft.SuikaApp.Utils.AppPersistence#loadData(java.io.File, java.lang.String, java.util.List) + /** + * Loads data from the given file into a DataStore object. + * + * @param file the file to load + * @return the loaded DataStore object */ @Override public DataStore loadData(File file) { @@ -136,9 +146,6 @@ public DataStore loadData(File file) { if (parsedObject == null) { throw new RuntimeException("Could not parse file '" + file.getPath() + "' as a DataStore ."); - // LoggingUtils.msgInfo("Could not parse file '" + file.getPath() - // + "' into a OptionSetup object."); - // return null; } // Set AppPersistence @@ -150,13 +157,6 @@ public DataStore loadData(File file) { // If no definition defined, show warning and return parsed object if (definition == null) { - // LoggingUtils - // .msgWarn( - // "Using XmlPersistence without a StoreDefinition, customizations to the keys (e.g., KeyPanels, custom - // getters) will be lost"); - // parsedObject.setSetupFile(file); - // When loading DataStore, use absolute paths - return parsedObject; } @@ -175,16 +175,9 @@ public DataStore loadData(File file) { // ParsedObject is not a properly constructed DataStore, it only has its name and the values // Do not use it as a normal DataStore - // When loading DataStore, use absolute paths - // parsedObject.set(JOptionKeys.CURRENT_FOLDER_PATH, file.getAbsoluteFile().getParent()); - // parsedObject.set(JOptionKeys.USE_RELATIVE_PATHS, false); - - // System.out.println("PARSED OBJECT FOLDER:" + parsedObject.get(JOptionKeys.CURRENT_FOLDER_PATH)); - // Set values for (DataKey dataKey : definition.getKeys()) { Optional value = parsedObject.getTry(dataKey); - // Object value = parsedObject.getValuesMap().get(dataKey.getName()); if (value.isPresent()) { dataStore.setRaw(dataKey, value.get()); @@ -194,18 +187,16 @@ public DataStore loadData(File file) { // Set configuration file information dataStore.set(AppKeys.CONFIG_FILE, file.getAbsoluteFile()); dataStore.set(JOptionKeys.CURRENT_FOLDER_PATH, Optional.of(file.getAbsoluteFile().getParent())); - // dataStore.set(JOptionKeys.USE_RELATIVE_PATHS, false); - - // dataStore.set(parsedObject); - // dataStore.getKeys().stream() - // .forEach(key -> System.out.println("DATASTORE PANEL2:" + key.getKeyPanelProvider())); - // Set setup file - // parsedObject.getSetupFile().setFile(file); - // dataStore.setSetupFile(file); return dataStore; } + /** + * Loads custom properties from the given file. + * + * @param file the file to load + * @return the loaded DataStore object + */ private DataStore loadCustomProperties(File file) { DataStore baseData = DataStore.newInstance(definition); @@ -250,6 +241,9 @@ private DataStore loadCustomProperties(File file) { return baseData; } + /** + * Represents a custom property with a name and value. + */ static class CustomProperty { private final String name; private final String value; @@ -267,6 +261,12 @@ public String getValue() { return value; } + /** + * Parses a custom property from a line of text. + * + * @param line the line to parse + * @return the parsed CustomProperty + */ public static CustomProperty parse(String line) { String[] args = line.split("="); Preconditions.checkArgument(args.length == 2, "Expected 2 arguments, got " + args.length); @@ -275,6 +275,12 @@ public static CustomProperty parse(String line) { } } + /** + * Parses a line of custom properties and updates the given DataStore. + * + * @param line the line to parse + * @param baseData the DataStore to update + */ private void parseCustomPropertiesLine(String line, DataStore baseData) { if (line.startsWith("//")) { return; @@ -287,6 +293,12 @@ private void parseCustomPropertiesLine(String line, DataStore baseData) { baseData.setString(key, prop.getValue()); } + /** + * Loads setup data from the given file. + * + * @param file the file to load + * @return the loaded DataStore object + */ private DataStore loadSetupData(File file) { SpecsLogs.msgInfo("!Found old version of configuration file, trying to translate it"); SetupData parsedObject = XStreamUtils.read(file, new SetupDataXml()); @@ -312,29 +324,30 @@ private DataStore loadSetupData(File file) { data.set(key, key.getDecoder().get().decode(rawValue)); } - // Set setup file - // parsedObject.setSetupFile(file); - // data.setSetupFile(file); - return data; } - // public static ObjectXml getObjectXml(Collection> keys) { + /** + * Creates an ObjectXml instance for the given StoreDefinition. + * + * @param storeDefinition the StoreDefinition to use + * @return the created ObjectXml instance + */ public static ObjectXml getObjectXml(StoreDefinition storeDefinition) { return new DataStoreXml(storeDefinition); } - /* (non-Javadoc) - * @see org.suikasoft.SuikaApp.Utils.AppPersistence#saveData(java.io.File, org.suikasoft.jOptions.OptionSetup, boolean) + /** + * Saves the given DataStore to the specified file. + * + * @param file the file to save to + * @param data the DataStore to save + * @param keepConfigFile whether to keep the configuration file + * @return true if the save was successful, false otherwise */ @Override public boolean saveData(File file, DataStore data, boolean keepConfigFile) { - // Reset setup file - // if (!keepConfigFile) { - // data.setSetupFile((SetupFile) null); - // } - // When saving, set config file and use relative paths data.set(AppKeys.CONFIG_FILE, file.getAbsoluteFile()); data.set(JOptionKeys.CURRENT_FOLDER_PATH, Optional.of(file.getAbsoluteFile().getParent())); @@ -342,7 +355,6 @@ public boolean saveData(File file, DataStore data, boolean keepConfigFile) { // DataStore to write. Use same name to avoid conflicts DataStore storeToSave = getDataStoreToSave(data); - // DataStore storeToSave = DataStore.newInstance(data.getName(), data); boolean result = XStreamUtils.write(file, storeToSave, xmlMappings); @@ -355,6 +367,12 @@ public boolean saveData(File file, DataStore data, boolean keepConfigFile) { } + /** + * Creates a DataStore to save based on the given DataStore. + * + * @param data the DataStore to base on + * @return the created DataStore + */ public static DataStore getDataStoreToSave(DataStore data) { Optional def = data.getStoreDefinitionTry(); @@ -365,11 +383,7 @@ public static DataStore getDataStoreToSave(DataStore data) { DataStore storeToSave = DataStore.newInstance(data.getName()); for (DataKey key : def.get().getKeys()) { - // Before it was not being check if key existed or not, and added default values. - // Will it break stuff not putting the default values? if (data.hasValue(key)) { - // Should not encode values before saving, this will be a normal DataStore - // XStream will try to serialize the contents storeToSave.setRaw(key, data.get(key)); } } diff --git a/jOptions/src/org/suikasoft/jOptions/storedefinition/AStoreDefinition.java b/jOptions/src/org/suikasoft/jOptions/storedefinition/AStoreDefinition.java index 56d349b1..d830d0a2 100644 --- a/jOptions/src/org/suikasoft/jOptions/storedefinition/AStoreDefinition.java +++ b/jOptions/src/org/suikasoft/jOptions/storedefinition/AStoreDefinition.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.storedefinition; @@ -24,37 +24,36 @@ import org.suikasoft.jOptions.Datakey.DataKey; import org.suikasoft.jOptions.Interfaces.DataStore; +/** + * Abstract base class for {@link StoreDefinition} implementations. + */ public abstract class AStoreDefinition implements StoreDefinition { private final String appName; - // private final List> options; private final List sections; private final DataStore defaultData; - // private final transient Map> keyMap = new HashMap<>(); private final Map> keyMap = new HashMap<>(); /** - * @param appName - * @param options + * Creates a new store definition with the given name and options. + * + * @param appName the name of the store + * @param options the list of keys */ protected AStoreDefinition(String appName, List> options) { this(appName, Arrays.asList(StoreSection.newInstance(options)), null); } - // protected AStoreDefinition(String appName, List> options, DataStore defaultData) { - // - // options = parseOptions(options); - // - // this.appName = appName; - // this.options = options; - // this.defaultData = defaultData; - // } - + /** + * Creates a new store definition with the given name, sections, and default data. + * + * @param appName the name of the store + * @param sections the sections + * @param defaultData the default data + */ protected AStoreDefinition(String appName, List sections, DataStore defaultData) { check(sections); - this.appName = appName; - // To control list, and avoid Arrays.asList(), which is not serializable by XStream this.sections = new ArrayList<>(sections); this.defaultData = defaultData; } @@ -64,54 +63,23 @@ public Map> getKeyMap() { if (keyMap.isEmpty()) { keyMap.putAll(StoreDefinition.super.getKeyMap()); } - return keyMap; } /** - * Checks if all options have different names. - * - * @param options - * @return - */ - /* - private static List> parseOptions(List> options) { - Map> optionMap = FactoryUtils.newLinkedHashMap(); - - for (DataKey def : options) { - DataKey previousDef = optionMap.get(def.getName()); - if (previousDef != null) { - LoggingUtils.msgWarn("DataKey name clash between '" + previousDef - + "' and '" + def + "'"); - continue; - } - - optionMap.put(def.getName(), def); - } - - return FactoryUtils.newArrayList(optionMap.values()); - } - */ - - /** - * Checks if all options have different names. - * - * @param options - * @return + * Checks if all sections have different key names. + * + * @param sections the sections to check */ private static void check(List sections) { - Set seenKeys = new HashSet<>(); - - List> options = StoreSection.getAllKeys(sections); - - for (DataKey def : options) { - if (seenKeys.contains(def.getName())) { - throw new RuntimeException("DataKey clash for name '" + def.getName() + "'"); + Set keyNames = new HashSet<>(); + for (StoreSection section : sections) { + for (DataKey key : section.getKeys()) { + if (!keyNames.add(key.getName())) { + throw new RuntimeException("Duplicate key name: '" + key.getName() + "'"); + } } - - seenKeys.add(def.getName()); } - } @Override @@ -119,23 +87,36 @@ public String getName() { return appName; } + /** + * Retrieves all keys from the store definition. + * + * @return a list of keys + */ @Override public List> getKeys() { return StoreSection.getAllKeys(sections); } + /** + * Retrieves all sections from the store definition. + * + * @return a list of sections + */ @Override public List getSections() { return sections; } + /** + * Retrieves the default values for the store definition. + * + * @return the default data store + */ @Override public DataStore getDefaultValues() { if (defaultData != null) { return defaultData; } - return StoreDefinition.super.getDefaultValues(); } - } diff --git a/jOptions/src/org/suikasoft/jOptions/storedefinition/GenericStoreDefinition.java b/jOptions/src/org/suikasoft/jOptions/storedefinition/GenericStoreDefinition.java index dfb3d92f..d8756c48 100644 --- a/jOptions/src/org/suikasoft/jOptions/storedefinition/GenericStoreDefinition.java +++ b/jOptions/src/org/suikasoft/jOptions/storedefinition/GenericStoreDefinition.java @@ -1,14 +1,14 @@ /** * Copyright 2013 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.storedefinition; @@ -18,16 +18,28 @@ import org.suikasoft.jOptions.Datakey.DataKey; import org.suikasoft.jOptions.Interfaces.DataStore; +/** + * Default implementation of {@link StoreDefinition}. + */ public class GenericStoreDefinition extends AStoreDefinition { /** - * @param appName - * @param options + * Creates a new store definition with the given name and options. + * + * @param appName the name of the store + * @param options the list of keys */ GenericStoreDefinition(String appName, List> options) { super(appName, options); } + /** + * Creates a new store definition with the given name, sections, and default values. + * + * @param appName the name of the store + * @param sections the sections + * @param defaultValues the default values + */ GenericStoreDefinition(String appName, List sections, DataStore defaultValues) { super(appName, sections, defaultValues); } diff --git a/jOptions/src/org/suikasoft/jOptions/storedefinition/GenericStoreSection.java b/jOptions/src/org/suikasoft/jOptions/storedefinition/GenericStoreSection.java index ea84d751..7d67c61c 100644 --- a/jOptions/src/org/suikasoft/jOptions/storedefinition/GenericStoreSection.java +++ b/jOptions/src/org/suikasoft/jOptions/storedefinition/GenericStoreSection.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.storedefinition; @@ -18,24 +18,43 @@ import org.suikasoft.jOptions.Datakey.DataKey; +/** + * Default implementation of {@link StoreSection}. + */ class GenericStoreSection implements StoreSection { private final String name; private final List> keys; + /** + * Creates a new section with the given name and keys. + * + * @param name the section name + * @param keys the keys in the section + */ public GenericStoreSection(String name, List> keys) { - this.name = name; - this.keys = keys; + this.name = name; + this.keys = keys; } + /** + * Retrieves the name of the section. + * + * @return an {@link Optional} containing the section name, or empty if the name is null + */ @Override public Optional getName() { - return Optional.ofNullable(name); + return Optional.ofNullable(name); } + /** + * Retrieves the keys of the section. + * + * @return a list of {@link DataKey} objects representing the keys in the section + */ @Override public List> getKeys() { - return keys; + return keys; } } diff --git a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinition.java b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinition.java index 651cecc0..6f177139 100644 --- a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinition.java +++ b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinition.java @@ -1,14 +1,14 @@ /** * Copyright 2015 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.storedefinition; @@ -28,74 +28,93 @@ /** * Represents a configuration, based on a name and a set of keys. - * - * @author JoaoBispo - * */ public interface StoreDefinition { + /** + * Returns the name of the store definition. + * + * @return the name + */ String getName(); /** - * TODO: Return Collection instead of List - * - * @return + * Returns the list of keys in this store definition. + * + * @return the list of keys */ List> getKeys(); /** - * Sections of the StoreDefinition. - * - *

    - * By default, returns a List with a single unnamed section. - * - * @return + * Returns the sections of the store definition. By default, returns a list with a single unnamed section. + * + * @return the sections */ default List getSections() { return Arrays.asList(StoreSection.newInstance(getKeys())); } /** - * Maps the name of the key to the key itself. - * - * @return + * Returns a map from key name to DataKey, maintaining the order of the keys. + * + * @return the key map */ default Map> getKeyMap() { - // To maintain the order of the keys - // Map> keyMap = new LinkedHashMap<>(); - // getKeys().stream().forEach(key -> keyMap.put(getName(), key)); - // - // return keyMap; return getKeys().stream() .collect(Collectors.toMap(key -> key.getName(), key -> key)); } + /** + * Creates a new StoreDefinition from an enum class implementing DataKeyProvider. + * + * @param aClass the enum class + * @return a new StoreDefinition + */ public static & DataKeyProvider> StoreDefinition newInstance(Class aClass) { List> keys = new ArrayList<>(); for (T key : aClass.getEnumConstants()) { keys.add(key.getDataKey()); } - return new GenericStoreDefinition(aClass.getSimpleName(), keys); } + /** + * Creates a new StoreDefinition with the given name and keys. + * + * @param name the name + * @param keys the keys + * @return a new StoreDefinition + */ public static StoreDefinition newInstance(String name, DataKey... keys) { return new GenericStoreDefinition(name, Arrays.asList(keys)); } + /** + * Creates a new StoreDefinition with the given name and collection of keys. + * + * @param appName the name + * @param keys the keys + * @return a new StoreDefinition + */ public static GenericStoreDefinition newInstance(String appName, Collection> keys) { return new GenericStoreDefinition(appName, new ArrayList<>(keys)); } + /** + * Creates a new StoreDefinition from a class interface. + * + * @param aClass the class + * @return a new StoreDefinition + */ public static GenericStoreDefinition newInstanceFromInterface(Class aClass) { return StoreDefinition.newInstance(aClass.getSimpleName(), StoreDefinitions.fromInterface(aClass).getKeys()); } /** - * - * @param key - * @return the datakey with the same name as the given String. Throws an exception if no key is found with the given - * name + * Returns the DataKey with the given name. Throws an exception if not found. + * + * @param key the key name + * @return the DataKey */ default DataKey getKey(String key) { DataKey dataKey = getKeyMap().get(key); @@ -106,11 +125,22 @@ default DataKey getKey(String key) { return dataKey; } + /** + * Returns the raw DataKey with the given name. Throws an exception if not found. + * + * @param key the key name + * @return the raw DataKey + */ @SuppressWarnings("unchecked") // It is always Object default DataKey getKeyRaw(String key) { return (DataKey) getKey(key); } + /** + * Returns a DataStore with the default values for this store definition. + * + * @return the DataStore with default values + */ default DataStore getDefaultValues() { DataStore data = DataStore.newInstance(getName()); @@ -123,10 +153,22 @@ default DataStore getDefaultValues() { return data; } + /** + * Sets the default values in the given DataStore. + * + * @param data the DataStore + * @return the updated StoreDefinition + */ default StoreDefinition setDefaultValues(DataStore data) { throw new NotImplementedException(getClass()); } + /** + * Checks if the store definition contains a key with the given name. + * + * @param keyName the key name + * @return true if the key exists, false otherwise + */ default boolean hasKey(String keyName) { return getKeyMap().containsKey(keyName); } diff --git a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitionBuilder.java b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitionBuilder.java index be23b93c..75196225 100644 --- a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitionBuilder.java +++ b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitionBuilder.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.storedefinition; @@ -25,77 +25,80 @@ import pt.up.fe.specs.util.SpecsLogs; +/** + * Builder for creating {@link StoreDefinition} instances, supporting sections and default data. + */ public class StoreDefinitionBuilder { + /** The name of the application or store. */ private final String appName; - // private final List> options; + /** The list of sections in the store definition. */ private final List sections; + /** The section currently being built. */ private StoreSectionBuilder currentSection; - + /** Set of key names already added, to avoid duplicates. */ private final Set addedKeys; + /** Default data for the store definition. */ private DataStore defaultData; - // private StoreDefinitionBuilder(String appName, Set addedKeys, - // DataStore defaultData) { - // - // //super(appName, new ArrayList<>()); - // this.appName = appName; - // this. - // - // this.addedKeys = addedKeys; - // this.defaultData = defaultData; - // } - - // private StoreDefinitionBuilder(String appName, List> options, Set addedKeys, - // DataStore defaultData) { - // - // this.appName = appName; - // this.options = options; - // this.addedKeys = addedKeys; - // this.defaultData = defaultData; - // } - + /** + * Creates a new builder for the given application name. + * + * @param appName the name of the application or store + */ public StoreDefinitionBuilder(String appName) { this.appName = appName; sections = new ArrayList<>(); currentSection = null; - addedKeys = new HashSet<>(); defaultData = null; - // this(appName, new ArrayList<>(), new HashSet<>(), null); } + /** + * Creates a new builder and adds a definition from the given class interface. + * + * @param aClass the class to extract a definition from + */ public StoreDefinitionBuilder(Class aClass) { this(aClass.getSimpleName()); - addDefinition(StoreDefinitions.fromInterface(aClass)); } /** - * Helper method to add several keys. - * - * @param keys - * @return + * Adds several keys to the current section or store. + * + * @param keys the keys to add + * @return this builder */ public StoreDefinitionBuilder addKeys(DataKey... keys) { return addKeys(Arrays.asList(keys)); } + /** + * Adds a collection of keys to the current section or store. + * + * @param keys the keys to add + * @return this builder + */ public StoreDefinitionBuilder addKeys(Collection> keys) { for (DataKey key : keys) { addKey(key); } - return this; } + /** + * Adds a single key to the current section or store. + * + * @param key the key to add + * @return this builder + */ public StoreDefinitionBuilder addKey(DataKey key) { // Check if key is not already added if (addedKeys.contains(key.getName())) { SpecsLogs.warn("Duplicated key while building Store Definition: '" + key.getName() + "'"); return this; } - addedKeys.add(key.getName()); // Section logic @@ -108,6 +111,12 @@ public StoreDefinitionBuilder addKey(DataKey key) { return this; } + /** + * Starts a new section with the given name. + * + * @param name the name of the section + * @return this builder + */ public StoreDefinitionBuilder startSection(String name) { // If current section not null, store it if (currentSection != null) { @@ -120,27 +129,22 @@ public StoreDefinitionBuilder startSection(String name) { return this; } - /* - @Override - public DataStore getDefaultValues() { - if (defaultData != null) { - return defaultData; - } - - return super.getDefaultValues(); - } - */ - - // @Override + /** + * Sets the default values for the store definition. + * + * @param data the default data + * @return this builder + */ public StoreDefinitionBuilder setDefaultValues(DataStore data) { defaultData = data; return this; - // StoreDefinitionBuilder builder = new StoreDefinitionBuilder(getName(), addedKeys, data); - // builder.getKeys().addAll(getKeys()); - // - // return builder; } + /** + * Builds the store definition. + * + * @return the constructed {@link StoreDefinition} + */ public StoreDefinition build() { // Save last section if (currentSection != null) { @@ -150,22 +154,47 @@ public StoreDefinition build() { return new GenericStoreDefinition(appName, sections, defaultData); } + /** + * Adds a store definition to this builder. + * + * @param storeDefinition the store definition to add + * @return this builder + */ public StoreDefinitionBuilder addDefinition(StoreDefinition storeDefinition) { addDefinitionPrivate(storeDefinition, false); return this; } + /** + * Adds a named store definition to this builder. + * + * @param name the name of the store definition + * @param storeDefinition the store definition to add + * @return this builder + */ public StoreDefinitionBuilder addNamedDefinition(String name, StoreDefinition storeDefinition) { return addNamedDefinition(new StoreDefinitionBuilder(name).addDefinition(storeDefinition).build()); } + /** + * Adds a named store definition to this builder. + * + * @param storeDefinition the store definition to add + * @return this builder + */ public StoreDefinitionBuilder addNamedDefinition(StoreDefinition storeDefinition) { addDefinitionPrivate(storeDefinition, true); return this; } + /** + * Adds a store definition to this builder, optionally using its name as a section name. + * + * @param storeDefinition the store definition to add + * @param useName whether to use the store definition's name as a section name + */ private void addDefinitionPrivate(StoreDefinition storeDefinition, boolean useName) { if (useName) { startSection(storeDefinition.getName()); diff --git a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitionIndexes.java b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitionIndexes.java index 90e00c16..a5e60101 100644 --- a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitionIndexes.java +++ b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitionIndexes.java @@ -1,14 +1,14 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.storedefinition; @@ -20,42 +20,67 @@ import org.suikasoft.jOptions.Datakey.DataKey; /** - * Maps keys of a StoreDefinition to an index. - * - * @author JoaoBispo - * + * Maps keys of a {@link StoreDefinition} to an index. */ public class StoreDefinitionIndexes { private final Map keysToIndexes; + /** + * Builds the index map for the given store definition. + * + * @param definition the store definition + */ public StoreDefinitionIndexes(StoreDefinition definition) { this.keysToIndexes = new HashMap<>(); - List> keys = definition.getKeys(); for (int i = 0; i < keys.size(); i++) { keysToIndexes.put(keys.get(i).getName(), i); } } + /** + * Returns the index of the given key. + * + * @param key the key + * @return the index + * @throws RuntimeException if the key is not present + */ public int getIndex(DataKey key) { return getIndex(key.getName()); } + /** + * Returns the index of the key with the given name. + * + * @param key the key name + * @return the index + * @throws RuntimeException if the key is not present + */ public int getIndex(String key) { Integer index = keysToIndexes.get(key); - if (index == null) { throw new RuntimeException("Key '" + key + "' not present in this definition: " + keysToIndexes.keySet()); } - return index; } + /** + * Checks if the given key is present in the index map. + * + * @param key the key + * @return true if present, false otherwise + */ public boolean hasIndex(DataKey key) { return hasIndex(key.getName()); } + /** + * Checks if the key with the given name is present in the index map. + * + * @param key the key name + * @return true if present, false otherwise + */ public boolean hasIndex(String key) { return keysToIndexes.containsKey(key); } diff --git a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitionProvider.java b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitionProvider.java index 1641c125..f0b6e835 100644 --- a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitionProvider.java +++ b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitionProvider.java @@ -1,27 +1,29 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.storedefinition; /** - * Returns a StoreDefinition. - * - * @author JoaoBispo - * + * Functional interface for providing a {@link StoreDefinition}. */ @FunctionalInterface public interface StoreDefinitionProvider { + /** + * Returns a store definition. + * + * @return the store definition + */ StoreDefinition getStoreDefinition(); } diff --git a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitions.java b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitions.java index 33425fee..ef979844 100644 --- a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitions.java +++ b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreDefinitions.java @@ -1,14 +1,14 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.storedefinition; @@ -20,56 +20,58 @@ import pt.up.fe.specs.util.utilities.CachedItems; +/** + * Utility class for building {@link StoreDefinition} instances from Java interfaces. + */ public class StoreDefinitions { private static final boolean ENABLE_STORE_DEFINITIONS_CACHE = true; - - // Using ThreadLocal to avoid using a thread-safe class. Since this is a cache, there is no problem in recomputing - // the values for each thread. - // private static final ThreadLocal, StoreDefinition>> STORE_DEFINITIONS_CACHE = ThreadLocal - // .withInitial(() -> new CachedItems<>(StoreDefinitions::fromInterfacePrivate)); - private static final CachedItems, StoreDefinition> STORE_DEFINITIONS_CACHE = new CachedItems<>( StoreDefinitions::fromInterfacePrivate, true); + /** + * Returns the cache for store definitions. + * + * @return the cache + */ public static CachedItems, StoreDefinition> getStoreDefinitionsCache() { return STORE_DEFINITIONS_CACHE; } /** * Collects all public static DataKey fields and builds a StoreDefinition with those fields. - * - * @param aClass - * @return + * + * @param aClass the class to extract DataKeys from + * @return a StoreDefinition with the DataKeys from the class */ public static StoreDefinition fromInterface(Class aClass) { if (ENABLE_STORE_DEFINITIONS_CACHE) { return getStoreDefinitionsCache().get(aClass); } - return fromInterfacePrivate(aClass); } + /** + * Private method to collect all public static DataKey fields and build a StoreDefinition. + * + * @param aClass the class to extract DataKeys from + * @return a StoreDefinition with the DataKeys from the class + */ private static StoreDefinition fromInterfacePrivate(Class aClass) { - StoreDefinitionBuilder builder = new StoreDefinitionBuilder(aClass.getSimpleName()); - for (Field field : aClass.getFields()) { if (!DataKey.class.isAssignableFrom(field.getType())) { continue; } - if (!Modifier.isStatic(field.getModifiers())) { continue; } - try { builder.addKey((DataKey) field.get(null)); } catch (Exception e) { throw new RuntimeException("Could not retrive value of field: " + field); } } - return builder.build(); } diff --git a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreSection.java b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreSection.java index 4f144c28..76c5791a 100644 --- a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreSection.java +++ b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreSection.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.storedefinition; @@ -19,36 +19,55 @@ import org.suikasoft.jOptions.Datakey.DataKey; +/** + * Represents a section in a store definition, grouping related keys. + */ public interface StoreSection { /** - * - * @return the name of the section + * Returns the name of the section, if present. + * + * @return the name of the section, or empty if unnamed */ Optional getName(); /** - * + * Returns the keys of the section. + * * @return the keys of the section */ List> getKeys(); + /** + * Creates a new section with the given name and keys. + * + * @param name the section name + * @param keys the keys in the section + * @return a new StoreSection instance + */ static StoreSection newInstance(String name, List> keys) { - return new GenericStoreSection(name, keys); + return new GenericStoreSection(name, keys); } + /** + * Creates a new unnamed section with the given keys. + * + * @param keys the keys in the section + * @return a new StoreSection instance + */ static StoreSection newInstance(List> keys) { - return newInstance(null, keys); + return newInstance(null, keys); } /** - * - * @param sections - * @return a list with all the keys of the given sections + * Returns a list with all the keys of the given sections. + * + * @param sections the sections to extract keys from + * @return a list with all keys from the sections */ static List> getAllKeys(List sections) { - return sections.stream() - .flatMap(section -> section.getKeys().stream()) - .collect(Collectors.toList()); + return sections.stream() + .flatMap(section -> section.getKeys().stream()) + .collect(Collectors.toList()); } } diff --git a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreSectionBuilder.java b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreSectionBuilder.java index d7b7198c..e4be08a2 100644 --- a/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreSectionBuilder.java +++ b/jOptions/src/org/suikasoft/jOptions/storedefinition/StoreSectionBuilder.java @@ -1,14 +1,14 @@ /** * Copyright 2016 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.storedefinition; @@ -20,36 +20,56 @@ import org.suikasoft.jOptions.Datakey.DataKey; +/** + * Builder for creating {@link StoreSection} instances. + */ public class StoreSectionBuilder { private final String name; private final List> keys; private final Set keysCheck; + /** + * Creates a new builder for an unnamed section. + */ public StoreSectionBuilder() { - this(null); + this(null); } + /** + * Creates a new builder for a section with the given name. + * + * @param name the section name + */ public StoreSectionBuilder(String name) { - this.name = name; - keys = new ArrayList<>(); - keysCheck = new HashSet<>(); + this.name = name; + keys = new ArrayList<>(); + keysCheck = new HashSet<>(); } + /** + * Adds a key to the section. + * + * @param key the key to add + * @return this builder + * @throws RuntimeException if a key with the same name already exists + */ public StoreSectionBuilder add(DataKey key) { - // Check - if (keysCheck.contains(key.getName())) { - throw new RuntimeException("Datakey clash for name '" + key.getName() + "'"); - } - keysCheck.add(key.getName()); - - keys.add(key); - - return this; + if (keysCheck.contains(key.getName())) { + throw new RuntimeException("Datakey clash for name '" + key.getName() + "'"); + } + keysCheck.add(key.getName()); + keys.add(key); + return this; } + /** + * Builds the section. + * + * @return the built StoreSection + */ public StoreSection build() { - return StoreSection.newInstance(name, keys); + return StoreSection.newInstance(name, keys); } } diff --git a/jOptions/src/org/suikasoft/jOptions/streamparser/GenericLineStreamParser.java b/jOptions/src/org/suikasoft/jOptions/streamparser/GenericLineStreamParser.java index d1562828..0f25cd68 100644 --- a/jOptions/src/org/suikasoft/jOptions/streamparser/GenericLineStreamParser.java +++ b/jOptions/src/org/suikasoft/jOptions/streamparser/GenericLineStreamParser.java @@ -1,14 +1,14 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.streamparser; @@ -21,74 +21,105 @@ import pt.up.fe.specs.util.utilities.LineStream; +/** + * Default implementation of {@link LineStreamParser}. + * + * @param the type of DataClass + */ class GenericLineStreamParser> implements LineStreamParser { private final T data; private final Map> workers; - private LineStream currentLineStream; private Predicate lineIgnore; private int numExceptions; + /** + * Creates a new parser with the given input data and workers. + * + * @param inputData the initial data + * @param workers the map of worker IDs to workers + */ public GenericLineStreamParser(T inputData, Map> workers) { - // this.data = DataStore.newInstance("Generic LineStream Data").addAll(inputData); this.data = inputData; this.workers = workers; - - // Initialize data for each worker this.workers.values().forEach(worker -> worker.init(data)); - currentLineStream = null; lineIgnore = null; numExceptions = 0; } + /** + * Returns the number of exceptions encountered during parsing. + * + * @return the number of exceptions + */ @Override public int getNumExceptions() { return numExceptions; } + /** + * Returns the data associated with this parser. + * + * @return the data + */ @Override public T getData() { return data; } + /** + * Returns the predicate used to ignore lines. + * + * @return the line ignore predicate + */ @Override public Predicate getLineIgnore() { if (lineIgnore == null) { return string -> false; } - return lineIgnore; } + /** + * Parses the given line stream using the worker associated with the given ID. + * + * @param id the worker ID + * @param lineStream the line stream to parse + * @return true if the parsing was successful, false otherwise + */ @Override public boolean parse(String id, LineStream lineStream) { try { this.currentLineStream = lineStream; - LineStreamWorker worker = workers.get(id); if (worker == null) { - // Check if id should be ignored - // return getLineIgnore().test(id); return false; } - // System.out.println("Worker: " + id); worker.apply(lineStream, data); - return true; } catch (Exception e) { numExceptions++; throw e; } - } + /** + * Returns the collection of worker IDs. + * + * @return the worker IDs + */ @Override public Collection getIds() { return workers.keySet(); } + /** + * Closes all workers associated with this parser. + * + * @throws Exception if an error occurs during closing + */ @Override public void close() throws Exception { for (LineStreamWorker worker : workers.values()) { @@ -96,16 +127,31 @@ public void close() throws Exception { } } + /** + * Returns the number of lines read by the current line stream. + * + * @return the number of read lines + */ @Override public long getReadLines() { return currentLineStream.getReadLines(); } + /** + * Returns the number of characters read by the current line stream. + * + * @return the number of read characters + */ @Override public long getReadChars() { return currentLineStream.getReadChars(); } + /** + * Sets the predicate used to ignore lines. + * + * @param ignorePredicate the line ignore predicate + */ @Override public void setLineIgnore(Predicate ignorePredicate) { this.lineIgnore = ignorePredicate; diff --git a/jOptions/src/org/suikasoft/jOptions/streamparser/GenericLineStreamWorker.java b/jOptions/src/org/suikasoft/jOptions/streamparser/GenericLineStreamWorker.java index f22c1d84..c778f4b2 100644 --- a/jOptions/src/org/suikasoft/jOptions/streamparser/GenericLineStreamWorker.java +++ b/jOptions/src/org/suikasoft/jOptions/streamparser/GenericLineStreamWorker.java @@ -1,14 +1,14 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.streamparser; @@ -20,28 +20,56 @@ import pt.up.fe.specs.util.utilities.LineStream; +/** + * Default implementation of {@link LineStreamWorker}. + * + * @param the type of DataClass + */ class GenericLineStreamWorker> implements LineStreamWorker { private final String id; private final Consumer init; private final BiConsumer apply; + /** + * Creates a new worker with the given id, initializer, and apply function. + * + * @param id the worker id + * @param init the initializer + * @param apply the apply function + */ public GenericLineStreamWorker(String id, Consumer init, BiConsumer apply) { this.id = id; this.init = init; this.apply = apply; } + /** + * Gets the id of the worker. + * + * @return the worker id + */ @Override public String getId() { return id; } + /** + * Initializes the worker with the given data. + * + * @param data the data to initialize + */ @Override public void init(T data) { init.accept(data); } + /** + * Applies the worker logic to the given line stream and data. + * + * @param lineStream the line stream + * @param data the data + */ @Override public void apply(LineStream lineStream, T data) { apply.accept(lineStream, data); diff --git a/jOptions/src/org/suikasoft/jOptions/streamparser/LineStreamParser.java b/jOptions/src/org/suikasoft/jOptions/streamparser/LineStreamParser.java index 6b516ebb..8af80fe9 100644 --- a/jOptions/src/org/suikasoft/jOptions/streamparser/LineStreamParser.java +++ b/jOptions/src/org/suikasoft/jOptions/streamparser/LineStreamParser.java @@ -1,14 +1,14 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.streamparser; @@ -27,50 +27,66 @@ import pt.up.fe.specs.util.SpecsSystem; import pt.up.fe.specs.util.utilities.LineStream; +/** + * Interface for parsing data from a {@link pt.up.fe.specs.util.utilities.LineStream} into a {@link DataClass}. + * + * @param the type of DataClass + */ public interface LineStreamParser> extends AutoCloseable { - // static LineStreamParser newInstance(Map workers) { - // return newInstance(DataStore.newInstance("Empty DataStore"), workers); - // } - + /** + * Returns a new parser instance for the given input data and workers. + * + * @param inputData the initial data + * @param workers the map of worker IDs to workers + * @return a new LineStreamParser + */ static > LineStreamParser newInstance(T inputData, Map> workers) { - return new GenericLineStreamParser<>(inputData, workers); } /** * Returns a DataStore with the current parsed values. - * + * * @return DataStore with parsed data */ - public T getData(); + T getData(); /** * Applies a LineStreamWorker to the given LineStream, based on the given id. - * - * @param id - * @param lineStream - * @return true if the id was valid, false otherwise. When returning false, the LineStream remains unmodified + * + * @param id the worker id + * @param lineStream the line stream + * @return true if the id was valid, false otherwise */ - public boolean parse(String id, LineStream lineStream); + boolean parse(String id, LineStream lineStream); /** - * Each LineStreamWorker of this parser is associated to an id. This function returns the ids supported by this - * LineStreamParser. - * - * @return the LineStreamWorker ids supported by this parser + * Returns the IDs supported by this parser. + * + * @return the supported worker IDs */ - public Collection getIds(); + Collection getIds(); + /** + * Parses an input stream and optionally dumps unparsed lines to a file. + * + * @param inputStream the input stream + * @param dumpFile the file to dump unparsed lines + * @return lines of the inputStream that were not parsed + */ default String parse(InputStream inputStream, File dumpFile) { return parse(inputStream, dumpFile, true, true); } /** - * - * @param inputStream - * @param dumpFile + * Parses an input stream and optionally dumps unparsed lines to a file. + * + * @param inputStream the input stream + * @param dumpFile the file to dump unparsed lines + * @param printLinesNotParsed whether to print unparsed lines + * @param storeLinesNotParsed whether to store unparsed lines * @return lines of the inputStream that were not parsed */ default String parse(InputStream inputStream, File dumpFile, boolean printLinesNotParsed, @@ -128,11 +144,21 @@ default String parse(InputStream inputStream, File dumpFile, boolean printLinesN return linesNotParsed.toString(); } + /** + * Returns the number of lines read by the parser. + * + * @return the number of lines read + */ default long getReadLines() { SpecsLogs.debug("Not implemented yet, returning 0"); return 0; } + /** + * Returns the number of characters read by the parser. + * + * @return the number of characters read + */ default long getReadChars() { SpecsLogs.debug("Not implemented yet, returning 0"); return 0; @@ -140,23 +166,34 @@ default long getReadChars() { /** * Predicate that in case a line is not parsed, tests if it should be ignored. - * + * *

    * By default, always returns false (does not ignore lines). - * - * @return + * + * @return the predicate for ignoring lines */ default Predicate getLineIgnore() { return string -> false; } + /** + * Sets the predicate for ignoring lines. + * + * @param ignorePredicate the predicate for ignoring lines + */ void setLineIgnore(Predicate ignorePredicate); + /** + * Returns the number of exceptions that occurred during parsing. + * + * @return the number of exceptions + */ int getNumExceptions(); /** - * - * @return true, if at least one exception has occurred + * Returns whether at least one exception has occurred during parsing. + * + * @return true if at least one exception has occurred, false otherwise */ default boolean hasExceptions() { return getNumExceptions() > 0; diff --git a/jOptions/src/org/suikasoft/jOptions/streamparser/LineStreamParsers.java b/jOptions/src/org/suikasoft/jOptions/streamparser/LineStreamParsers.java index 2370e19b..0ea483fc 100644 --- a/jOptions/src/org/suikasoft/jOptions/streamparser/LineStreamParsers.java +++ b/jOptions/src/org/suikasoft/jOptions/streamparser/LineStreamParsers.java @@ -1,14 +1,14 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.streamparser; @@ -27,162 +27,222 @@ import pt.up.fe.specs.util.utilities.LineStream; /** - * - * Utility methods for parsing general-purpose information (e.g., a boolean, an enum) from a LineStream. - * - *

    - * TODO: Move to project jOptions, rename to LineStreamParsers. - * - * @author JoaoBispo - * + * Utility methods for parsing general-purpose information from a {@link pt.up.fe.specs.util.utilities.LineStream}. */ public class LineStreamParsers { + /** + * Parses a boolean from a string, accepting "1" as true and "0" as false. + * + * @param aBoolean the string to parse + * @return the boolean value + * @throws RuntimeException if the value is not "1" or "0" + */ public static boolean oneOrZero(String aBoolean) { if (aBoolean.equals("1")) { return true; } - if (aBoolean.equals("0")) { return false; } - throw new RuntimeException("Unexpected value: '" + aBoolean + "'"); } + /** + * Parses a boolean from the next line of the stream, accepting "1" as true and "0" as false. + * + * @param lines the line stream + * @return the boolean value + */ public static boolean oneOrZero(LineStream lines) { return oneOrZero(lines.nextLine()); } + /** + * Parses an integer from the next line of the stream. + * + * @param lines the line stream + * @return the integer value + */ public static int integer(LineStream lines) { return Integer.parseInt(lines.nextLine()); } + /** + * Parses a long from the next line of the stream. + * + * @param lines the line stream + * @return the long value + */ public static long longInt(LineStream lines) { return Long.parseLong(lines.nextLine()); } - /* - public static & StringProvider> T enumFromInt(EnumHelper helper, T defaultValue, - LineStream lines) { - - int index = parseInt(lines); - - if (index >= helper.getSize()) { - return defaultValue; - } - - return helper.valueOf(index); - } - */ + /** + * Parses an enum from its ordinal value in the next line of the stream. + * + * @param enumClass the class of the enum + * @param lines the line stream + * @return the enum value + */ public static > T enumFromOrdinal(Class enumClass, LineStream lines) { return SpecsEnums.fromOrdinal(enumClass, integer(lines)); } + /** + * Parses an enum from its name in the next line of the stream. + * + * @param enumClass the class of the enum + * @param lines the line stream + * @return the enum value + */ public static > T enumFromName(Class enumClass, LineStream lines) { return SpecsEnums.fromName(enumClass, lines.nextLine()); } + /** + * Parses an enum from its integer value in the next line of the stream. + * + * @param helper the enum helper + * @param lines the line stream + * @return the enum value + */ public static & StringProvider> T enumFromInt(EnumHelperWithValue helper, LineStream lines) { - return helper.fromValue(integer(lines)); } + /** + * Parses an enum from its name in the next line of the stream. + * + * @param helper the enum helper + * @param lines the line stream + * @return the enum value + */ public static & StringProvider> T enumFromName(EnumHelperWithValue helper, LineStream lines) { - return helper.fromName(lines.nextLine()); } + /** + * Parses an enum from its value in the next line of the stream. + * + * @param helper the enum helper + * @param lines the line stream + * @return the enum value + */ public static & StringProvider> T enumFromValue(EnumHelperWithValue helper, LineStream lines) { - String value = lines.nextLine(); return helper.fromValue(value); } /** - * First line represents the number of enums to parse, one in each succeeding line. - * - * @param helper - * @param lines - * @return + * Parses a list of enums from their names in the stream. The first line represents the number of enums to parse. + * + * @param helper the enum helper + * @param lines the line stream + * @return the list of enums */ public static & StringProvider> List enumListFromName(EnumHelperWithValue helper, LineStream lines) { - int numEnums = integer(lines); List enums = new ArrayList<>(numEnums); - for (int i = 0; i < numEnums; i++) { enums.add(enumFromName(helper, lines)); } - return enums; } + /** + * Checks for duplicate keys in a map and throws an exception if a duplicate is found. + * + * @param id the identifier + * @param key the key to check + * @param value the value associated with the key + * @param map the map to check + * @param the type of the key + */ public static void checkDuplicate(String id, K key, Object value, Map map) { Object currentObject = map.get(key); if (currentObject != null) { throw new RuntimeException("Duplicate value for id '" + id + "', key '" + key + "'.\nCurrent value:" + value + "\nPrevious value:" + currentObject); } - } + /** + * Checks for duplicate keys in a set and throws an exception if a duplicate is found. + * + * @param id the identifier + * @param key the key to check + * @param set the set to check + * @param the type of the key + */ public static void checkDuplicate(String id, K key, Set set) { if (set.contains(key)) { throw new RuntimeException("Duplicate value for id '" + id + "', key '" + key + "'"); } } + /** + * Parses a key-value pair from the stream and adds it to the map. + * + * @param id the identifier + * @param linestream the line stream + * @param stringMap the map to add the key-value pair + */ public static void stringMap(String id, LineStream linestream, Map stringMap) { String key = linestream.nextLine(); String value = linestream.nextLine(); - LineStreamParsers.checkDuplicate(id, key, value, stringMap); stringMap.put(key, value); } /** - * Overload that sets 'checkDuplicate' to true. - * - * @param id - * @param linestream - * @param stringSet + * Parses a string from the stream and adds it to the set, checking for duplicates. + * + * @param id the identifier + * @param linestream the line stream + * @param stringSet the set to add the string */ public static void stringSet(String id, LineStream linestream, Set stringSet) { stringSet(id, linestream, stringSet, true); } + /** + * Parses a string from the stream and adds it to the set. + * + * @param id the identifier + * @param linestream the line stream + * @param stringSet the set to add the string + * @param checkDuplicate whether to check for duplicates + */ public static void stringSet(String id, LineStream linestream, Set stringSet, boolean checkDuplicate) { String key = linestream.nextLine(); - if (checkDuplicate) { LineStreamParsers.checkDuplicate(id, key, stringSet); } - stringSet.add(key); } /** - * Overload which uses the second line as value. - * - * @param lines - * @param map + * Parses a key-value pair from the stream and adds it to the multi-map. + * + * @param lines the line stream + * @param map the multi-map to add the key-value pair */ public static void multiMap(LineStream lines, MultiMap map) { multiMap(lines, map, string -> string); } /** - * Reads two lines from LineStream, the first is the key, the second is the value. Applies the given decoder to the - * value. - * - * @param lines - * @param map - * @param decoder + * Parses a key-value pair from the stream, applies a decoder to the value, and adds it to the multi-map. + * + * @param lines the line stream + * @param map the multi-map to add the key-value pair + * @param decoder the decoder to apply to the value + * @param the type of the value */ public static void multiMap(LineStream lines, MultiMap map, Function decoder) { String key = lines.nextLine(); @@ -190,70 +250,58 @@ public static void multiMap(LineStream lines, MultiMap map, Funct } /** - * First line represents the number of elements of the list. - * - * @param lines - * @return + * Parses a list of strings from the stream. The first line represents the number of elements in the list. + * + * @param lines the line stream + * @return the list of strings */ public static List stringList(LineStream lines) { return stringList(lines, LineStream::nextLine); - // int numElements = integer(lines); - // - // List strings = new ArrayList<>(numElements); - // for (int i = 0; i < numElements; i++) { - // strings.add(lines.nextLine()); - // } - // - // return strings; } /** - * First line represents the number of elements of the list. Then, the given parser is applies as many times as the - * number of elements. - * - * @param lines - * @return + * Parses a list of strings from the stream using a custom parser. The first line represents the number of elements + * in the list. + * + * @param lines the line stream + * @param parser the custom parser + * @return the list of strings */ public static List stringList(LineStream lines, Function parser) { int numElements = integer(lines); - List strings = new ArrayList<>(numElements); for (int i = 0; i < numElements; i++) { strings.add(parser.apply(lines)); } - return strings; } /** - * First line represents the number of elements of the list. Then, the given parser is applies as many times as the - * number of elements. - * - * @param lines - * @param parser - * @return + * Parses a list of values from the stream using a custom parser. The first line represents the number of elements + * in the list. + * + * @param lines the line stream + * @param parser the custom parser + * @param the type of the values + * @return the list of values */ public static List list(LineStream lines, Function parser) { int numElements = integer(lines); - List values = new ArrayList<>(numElements); for (int i = 0; i < numElements; i++) { values.add(parser.apply(lines)); } - return values; } /** - * - * - * @param lines - * @param parser - * @return if next line is empty, return empty Optional, otherwise returns Optional with line + * Parses an optional string from the next line of the stream. If the line is empty, returns an empty optional. + * + * @param lines the line stream + * @return the optional string */ public static Optional optionalString(LineStream lines) { var line = lines.nextLine(); - return line.isEmpty() ? Optional.empty() : Optional.of(line); } } diff --git a/jOptions/src/org/suikasoft/jOptions/streamparser/LineStreamWorker.java b/jOptions/src/org/suikasoft/jOptions/streamparser/LineStreamWorker.java index f786079a..1d42f9f1 100644 --- a/jOptions/src/org/suikasoft/jOptions/streamparser/LineStreamWorker.java +++ b/jOptions/src/org/suikasoft/jOptions/streamparser/LineStreamWorker.java @@ -1,14 +1,14 @@ /** * Copyright 2018 SPeCS. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package org.suikasoft.jOptions.streamparser; @@ -20,45 +20,66 @@ import pt.up.fe.specs.util.utilities.LineStream; +/** + * Worker for parsing a section of a {@link pt.up.fe.specs.util.utilities.LineStream} into a {@link DataClass}. + * + * @param the type of DataClass + */ public interface LineStreamWorker> { + /** + * Creates a new worker with the given id, initializer, and apply function. + * + * @param id the worker id + * @param init the initializer + * @param apply the apply function + * @return a new LineStreamWorker + */ static > LineStreamWorker newInstance(String id, Consumer init, BiConsumer apply) { return new GenericLineStreamWorker<>(id, init, apply); } + /** + * Creates a new worker with the given id and apply function. + * + * @param id the worker id + * @param apply the apply function + * @return a new LineStreamWorker + */ static > LineStreamWorker newInstance(String id, BiConsumer apply) { - // Do nothing Consumer init = data -> { }; - return new GenericLineStreamWorker<>(id, init, apply); } /** - * Id of this worker, precedes lines to parse in LineStream. - * - * @return + * Returns the id of this worker. + * + * @return the worker id */ String getId(); /** - * Initializes any data worker might need (e.g. initial values in DataStore) + * Initializes any data the worker might need (e.g., initial values in DataStore). + * + * @param data the data to initialize */ void init(T data); /** - * Parses linestream - * - * @param lineStream - * @param data + * Parses the line stream and updates the data. + * + * @param lineStream the line stream + * @param data the data to update */ void apply(LineStream lineStream, T data); /** * Finalizes a worker, after all workers have been executed. By default, does nothing. + * + * @param data the data to finalize */ default void close(T data) { - } } diff --git a/jOptions/src/org/suikasoft/jOptions/treenode/ClassesService.java b/jOptions/src/org/suikasoft/jOptions/treenode/ClassesService.java index ba490ae6..6ef9329c 100644 --- a/jOptions/src/org/suikasoft/jOptions/treenode/ClassesService.java +++ b/jOptions/src/org/suikasoft/jOptions/treenode/ClassesService.java @@ -1,11 +1,11 @@ -/** - * Copyright 2018 SPeCS. - * +/* + * Copyright 2018 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -27,52 +27,62 @@ import pt.up.fe.specs.util.SpecsLogs; +/** + * Service for managing and discovering DataNode classes by name for AST nodes. + * + * @param the type of DataNode + */ public class ClassesService> { private final Collection astNodesPackages; private final Class baseClass; - // private final CustomClassnameMapper customClassMap; private final Map> autoClassMap; private final Set warnedClasses; private Class defaultClass; + /** + * Creates a new ClassesService instance. + * + * @param baseClass the base class for DataNode + * @param astNodesPackages the collection of packages to search for AST nodes + */ public ClassesService(Class baseClass, Collection astNodesPackages) { - this.baseClass = baseClass; this.astNodesPackages = astNodesPackages; - // this.customClassMap = customClassMap; this.autoClassMap = new HashMap<>(); this.warnedClasses = new HashSet<>(); - defaultClass = null; } - // public ClassesService(Class baseClass, Collection astNodesPackages) { - // this(baseClass, astNodesPackages, new CustomClassnameMapper()); - // } - + /** + * Creates a new ClassesService instance. + * + * @param baseClass the base class for DataNode + * @param astNodesPackages the packages to search for AST nodes + */ public ClassesService(Class baseClass, String... astNodesPackages) { this(baseClass, Arrays.asList(astNodesPackages)); } + /** + * Sets the default class to use when no matching class is found. + * + * @param defaultClass the default class + * @return the current instance + */ public ClassesService setDefaultClass(Class defaultClass) { this.defaultClass = defaultClass; return this; } - // public CustomClassnameMapper getCustomClassMap() { - // return customClassMap; - // } - + /** + * Retrieves the class corresponding to the given classname. + * + * @param classname the name of the class + * @return the class object + */ public Class getClass(String classname) { - - // // Try custom map - // Class dataNodeClass = customClassMap.getClass(classname); - // if (dataNodeClass != null) { - // return dataNodeClass; - // } - // Try cached nodes Class dataNodeClass = autoClassMap.get(classname); if (dataNodeClass != null) { @@ -83,12 +93,10 @@ public Class getClass(String classname) { dataNodeClass = discoverClass(classname); autoClassMap.put(classname, dataNodeClass); return dataNodeClass; - } private Class getClass(String classname, String fullClassname) { try { - // Get class Class aClass = Class.forName(fullClassname); // Check if class is a subtype of DataNode @@ -97,13 +105,11 @@ private Class getClass(String classname, String fullClassname) { + ") that is not a DataNode"); } - // Cast class object return aClass.asSubclass(baseClass); } catch (ClassNotFoundException e) { // No class found, return null return null; - // throw new RuntimeException("Could not map classname '" + classname + "' to a node class"); } } @@ -123,7 +129,6 @@ private Class discoverClass(String classname) { for (var astNodesPackage : astNodesPackages) { // Append nodeClassname to basePackage var fullClassname = astNodesPackage + "." + classname; - // System.out.println("TRYING CLASS " + fullClassname); var nodeClass = getClass(classname, fullClassname); if (nodeClass != null) { @@ -138,7 +143,6 @@ private Class discoverClass(String classname) { SpecsLogs.info("ClassesService: no node class found for name '" + classname + "', using default class '" + defaultClass + "'"); - } return defaultClass; @@ -146,59 +150,24 @@ private Class discoverClass(String classname) { // Throw exception if nothing works throw new RuntimeException("Could not map classname '" + classname + "' to a node class"); - - // try { - // // Get class - // Class aClass = Class.forName(fullClassname); - // - // // Check if class is a subtype of DataNode - // if (!baseClass.isAssignableFrom(aClass)) { - // throw new RuntimeException("Classname '" + classname + "' was converted to a (" + fullClassname - // + ") that is not a DataNode"); - // } - // - // // Cast class object - // return aClass.asSubclass(baseClass); - // - // } catch (ClassNotFoundException e) { - // // If default node class is defined, use that class - // if (defaultClass != null) { - // if (!warnedClasses.contains(classname)) { - // warnedClasses.add(classname); - // - // SpecsLogs.info("ClassesService: no node class found for name '" + classname - // + "', using default class '" + defaultClass + "'"); - // - // } - // - // return defaultClass; - // } - // - // throw new RuntimeException("Could not map classname '" + classname + "' to a node class"); - // } } - // private String simpleNameToFullName(String nodeClassname) { - // var customName = customSimpleNameToFullName(nodeClassname); - // - // if (customName != null) { - // return customName; - // } - // - // // By default, append nodeClassname to basePackage - // return basePackage + "." + nodeClassname; - // } - /** - * Override method if you want to define custom rules. Any case that returns null uses the default conversion. - * - * @param nodeClassname - * @return + * Override this method to define custom rules for mapping simple names to full names. + * + * @param nodeClassname the simple name of the node class + * @return the full name of the node class, or null if no custom mapping exists */ protected String customSimpleNameToFullName(String nodeClassname) { return null; } + /** + * Retrieves a builder function for creating instances of the given DataNode class. + * + * @param dataNodeClass the class of the DataNode + * @return a function that builds DataNode instances + */ public BiFunction, T> getNodeBuilder( Class dataNodeClass) { @@ -220,7 +189,5 @@ public BiFunction, T> getNodeBuilder( SpecsLogs.msgLib("Could not create constructor for DataNode:" + e.getMessage()); return null; } - } - } diff --git a/jOptions/src/org/suikasoft/jOptions/treenode/DataNode.java b/jOptions/src/org/suikasoft/jOptions/treenode/DataNode.java index a6f749b5..7a588d82 100644 --- a/jOptions/src/org/suikasoft/jOptions/treenode/DataNode.java +++ b/jOptions/src/org/suikasoft/jOptions/treenode/DataNode.java @@ -1,11 +1,11 @@ -/** - * Copyright 2018 SPeCS. - * +/* + * Copyright 2018 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -30,32 +30,45 @@ import pt.up.fe.specs.util.system.Copyable; import pt.up.fe.specs.util.treenode.ATreeNode; +/** + * Abstract base class for tree nodes that hold a DataStore and support DataClass and Copyable interfaces. + * + * @param the type of DataNode + */ public abstract class DataNode> extends ATreeNode implements DataClass, Copyable { private final DataStore data; private final DataClass dataClass; + /** + * Constructs a DataNode with the given data and children. + * + * @param data the DataStore associated with this node + * @param children the child nodes of this node + */ public DataNode(DataStore data, Collection children) { super(children); - // SpecsCheck.checkArgument(dataI instanceof ListDataStore, - // () -> "Expected ListDataStore, found " + dataI.getClass()); - this.data = data; // To avoid implementing methods again this.dataClass = new GenericDataClass<>(this.data); } + /** + * Retrieves the DataStore associated with this node. + * + * @return the DataStore + */ public DataStore getData() { - // protected DataStore getData() { return data; } /** - * - * @return the class of the base node class of the tree + * Returns the class of the base node class of the tree. + * + * @return the base class */ protected abstract Class getBaseClass(); @@ -73,12 +86,13 @@ public T get(DataKey key) { /** * Generic method for setting values. - * + * *

    * If null is passed as value, removes current value associated with given key. - * - * @param key - * @param value + * + * @param key the key to set + * @param value the value to set + * @return the current instance */ @Override public K set(DataKey key, E value) { @@ -123,11 +137,7 @@ protected K copyPrivate() { // Copy all data for (var key : getDataKeysWithValues()) { - // var stringValue = key.copy((Object) get(key)); - // var copyValue = key.decode(stringValue); - // newNode.setValue(key.getName(), copyValue); newNode.setValue(key.getName(), key.copyRaw(get(key))); - // newNode.setValue(key.getName(), get(key)); } return newNode; @@ -135,6 +145,12 @@ protected K copyPrivate() { /*** STATIC HELPER METHODS ***/ + /** + * Creates a new DataStore for the given node class. + * + * @param nodeClass the class of the node + * @return a new DataStore instance + */ public static , T extends K> DataStore newDataStore(Class nodeClass) { DataStore data = DataStore.newInstance(StoreDefinitions.fromInterface(nodeClass), true); return data; @@ -142,10 +158,10 @@ public static , T extends K> DataStore newDataStore(Class< /** * Creates a new node using the same data as this node. - * - * @param nodeClass - * @param children - * @return + * + * @param nodeClass the class of the node + * @param children the child nodes + * @return a new instance of the node */ public static , T extends K> K newInstance(Class nodeClass, List children) { @@ -174,6 +190,11 @@ public String toContentString() { return getData().toInlinedString(); } + /** + * Returns the system-specific newline character. + * + * @return the newline character + */ protected String ln() { return SpecsIo.getNewline(); } diff --git a/jOptions/src/org/suikasoft/jOptions/treenode/GenericDataNode.java b/jOptions/src/org/suikasoft/jOptions/treenode/GenericDataNode.java index 0466dcd7..ee171b16 100644 --- a/jOptions/src/org/suikasoft/jOptions/treenode/GenericDataNode.java +++ b/jOptions/src/org/suikasoft/jOptions/treenode/GenericDataNode.java @@ -1,11 +1,11 @@ -/** - * Copyright 2021 SPeCS. - * +/* + * Copyright 2021 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -17,24 +17,41 @@ import org.suikasoft.jOptions.Interfaces.DataStore; +/** + * Generic implementation of a DataNode for use when a specific node type is not required. + */ public class GenericDataNode extends DataNode { - + /** + * Constructs a GenericDataNode with the given DataStore and children. + * + * @param data the DataStore for this node + * @param children the child nodes + */ public GenericDataNode(DataStore data, Collection children) { super(data, children); } - + /** + * Constructs a GenericDataNode with a default DataStore and no children. + */ public GenericDataNode() { this(DataStore.newInstance("GenericDataNode"), null); } - + /** + * Returns this node instance. + * + * @return this node + */ @Override protected GenericDataNode getThis() { return this; } - + /** + * Returns the base class for this node type. + * + * @return the GenericDataNode class + */ @Override protected Class getBaseClass() { return GenericDataNode.class; } - } diff --git a/jOptions/src/org/suikasoft/jOptions/treenode/NodeFieldReplacer.java b/jOptions/src/org/suikasoft/jOptions/treenode/NodeFieldReplacer.java index e8bfd9f5..2fd672b3 100644 --- a/jOptions/src/org/suikasoft/jOptions/treenode/NodeFieldReplacer.java +++ b/jOptions/src/org/suikasoft/jOptions/treenode/NodeFieldReplacer.java @@ -1,11 +1,11 @@ -/** - * Copyright 2021 SPeCS. - * +/* + * Copyright 2021 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -22,6 +22,11 @@ import pt.up.fe.specs.util.exceptions.CaseNotDefinedException; +/** + * Utility for replacing fields in DataNode trees based on a replacement detector function. + * + * @param the type of DataNode + */ public class NodeFieldReplacer> { private final PropertyWithNodeManager manager; @@ -33,10 +38,10 @@ public class NodeFieldReplacer> { private long replacedNodes; /** - * - * @param replacementProvider - * if the node needs to be replaced, returns a different node wrapped by the optional, otherwise returns - * empty + * Constructs a NodeFieldReplacer with the given replacement detector function. + * + * @param replacementProvider if the node needs to be replaced, returns a different node wrapped by the optional, + * otherwise returns empty */ public NodeFieldReplacer(Function> replacementProvider) { manager = new PropertyWithNodeManager(); @@ -47,6 +52,12 @@ public NodeFieldReplacer(Function> replacementProvider) { this.replacedNodes = 0; } + /** + * Replaces fields in the given node based on the replacement detector function. + * + * @param node the node whose fields are to be replaced + * @param the type of the node + */ public void replaceFields(N node) { processedNodes++; @@ -75,6 +86,13 @@ public void replaceFields(N node) { } + /** + * Replaces nodes in a list field of the given node. + * + * @param node the node containing the list field + * @param key the key identifying the list field + * @param the type of the node + */ private void replaceList(N node, DataKey key) { @SuppressWarnings("unchecked") var clavaNodes = (List) node.get(key); @@ -95,6 +113,13 @@ private void replaceList(N node, DataKey key) { node.set(objectKey, newClavaNodes); } + /** + * Replaces a node in an optional field of the given node. + * + * @param node the node containing the optional field + * @param key the key identifying the optional field + * @param the type of the node + */ @SuppressWarnings("unchecked") private void replaceOptional(N node, DataKey key) { var value = ((Optional) node.get(key)).get(); @@ -105,6 +130,13 @@ private void replaceOptional(N node, DataKey key) { }); } + /** + * Replaces a node in a data node field of the given node. + * + * @param node the node containing the data node field + * @param key the key identifying the data node field + * @param the type of the node + */ @SuppressWarnings("unchecked") private void replaceDataNode(N node, DataKey key) { var value = node.getBaseClass().cast(node.get(key)); diff --git a/jOptions/src/org/suikasoft/jOptions/treenode/PropertyWithNodeManager.java b/jOptions/src/org/suikasoft/jOptions/treenode/PropertyWithNodeManager.java index 803386ec..acac421c 100644 --- a/jOptions/src/org/suikasoft/jOptions/treenode/PropertyWithNodeManager.java +++ b/jOptions/src/org/suikasoft/jOptions/treenode/PropertyWithNodeManager.java @@ -1,11 +1,11 @@ -/** - * Copyright 2021 SPeCS. - * +/* + * Copyright 2021 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -25,9 +25,10 @@ /** * Manages DataKey properties that return an instance of DataNode. - * - * @author JBispo * + * Provides methods to retrieve DataKeys associated with DataNode properties for a given node. + * + * @author JBispo */ public class PropertyWithNodeManager { @@ -39,9 +40,10 @@ public class PropertyWithNodeManager { private static final Map, List>> POSSIBLE_KEYS_WITH_NODES = new ConcurrentHashMap<>(); /** - * All keys that can potentially have DataNodes. - * - * @return + * Retrieves all keys that can potentially have DataNodes for a given node. + * + * @param node the DataNode instance + * @return a list of DataKeys that can potentially have DataNodes */ private > List> getPossibleKeysWithNodes(K node) { List> keys = POSSIBLE_KEYS_WITH_NODES.get(node.getClass()); @@ -55,6 +57,12 @@ private > List> getPossibleKeysWithNodes(K node return keys; } + /** + * Finds keys that map to DataNode instances for a given node. + * + * @param node the DataNode instance + * @return a list of DataKeys that map to DataNode instances + */ private static > List> findKeysWithNodes(K node) { List> keysWithNodes = new ArrayList<>(); @@ -72,9 +80,10 @@ private static > List> findKeysWithNodes(K node } /** - * Keys that currently have nodes assigned. + * Retrieves keys that currently have nodes assigned for a given node. * - * @return + * @param node the DataNode instance + * @return a list of DataKeys that currently have nodes assigned */ @SuppressWarnings("unchecked") public > List> getKeysWithNodes(K node) { @@ -136,8 +145,6 @@ public > List> getKeysWithNodes(K node) { } return keys; - // ClavaLog.info("Case not supported yet:" + keyWithNode); - } } diff --git a/jOptions/src/org/suikasoft/jOptions/treenode/PropertyWithNodeType.java b/jOptions/src/org/suikasoft/jOptions/treenode/PropertyWithNodeType.java index 228c66f5..cfa90446 100644 --- a/jOptions/src/org/suikasoft/jOptions/treenode/PropertyWithNodeType.java +++ b/jOptions/src/org/suikasoft/jOptions/treenode/PropertyWithNodeType.java @@ -1,11 +1,11 @@ -/** - * Copyright 2021 SPeCS. - * +/* + * Copyright 2021 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -18,13 +18,21 @@ import org.suikasoft.jOptions.Datakey.DataKey; +/** + * Enum representing the type of property associated with a DataNode key. + */ public enum PropertyWithNodeType { - DATA_NODE, OPTIONAL, LIST, NOT_FOUND; - + /** + * Determines the type of a DataKey for a given DataNode. + * + * @param node the DataNode + * @param key the DataKey + * @return the property type + */ public static PropertyWithNodeType getKeyType(DataNode node, DataKey key) { // DataNode keys if (node.getBaseClass().isAssignableFrom(key.getValueClass())) { @@ -40,8 +48,6 @@ public static PropertyWithNodeType getKeyType(DataNode node, DataKey key) if (List.class.isAssignableFrom(key.getValueClass())) { return LIST; } - return NOT_FOUND; } - } diff --git a/jOptions/src/org/suikasoft/jOptions/treenode/converter/NodeDataParser.java b/jOptions/src/org/suikasoft/jOptions/treenode/converter/NodeDataParser.java index e92b3d4f..2a027e15 100644 --- a/jOptions/src/org/suikasoft/jOptions/treenode/converter/NodeDataParser.java +++ b/jOptions/src/org/suikasoft/jOptions/treenode/converter/NodeDataParser.java @@ -1,11 +1,11 @@ -/** - * Copyright 2020 SPeCS. - * +/* + * Copyright 2020 SPeCS Research Group. + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -25,9 +25,10 @@ /** * Applies methods that generate DataStores, based on arbitrary inputs defined by a signature Method. - * - * @author JoaoBispo * + * Provides a registry of compatible static methods for parsing node data. + * + * @author JoaoBispo */ public class NodeDataParser { @@ -35,6 +36,12 @@ public class NodeDataParser { private final Map dataParsers; private final Set warnedNodes; + /** + * Constructs a NodeDataParser instance. + * + * @param defaultMethod the default method to use when no specific parser is found + * @param classesWithParsers a collection of classes containing parser methods + */ public NodeDataParser(Method defaultMethod, Collection> classesWithParsers) { this.defaultMethod = defaultMethod; this.dataParsers = new HashMap<>(); @@ -48,9 +55,14 @@ public NodeDataParser(Method defaultMethod, Collection> classesWithPars for (var classWithParsers : classesWithParsers) { addParsers(defaultMethod, classWithParsers); } - } + /** + * Adds parser methods from the given class to the registry. + * + * @param parserMethodSignature the signature method to validate compatibility + * @param classWithParsers the class containing parser methods + */ private void addParsers(Method parserMethodSignature, Class classWithParsers) { for (Method method : classWithParsers.getMethods()) { @@ -64,13 +76,13 @@ private void addParsers(Method parserMethodSignature, Class classWithParsers) // Map name of the method to the method class dataParsers.put(methodName, method); } - } /** - * - * @param method - * @param signature + * Validates if the given method is compatible with the signature method. + * + * @param method the method to validate + * @param signature the signature method to compare against * @return true if both methods are considered equivalent */ private boolean isValidMethod(Method method, Method signature) { @@ -84,7 +96,7 @@ private boolean isValidMethod(Method method, Method signature) { return false; } - // For each paramters, check if they are assignable + // For each parameter, check if they are assignable var methodParams = method.getParameterTypes(); var signatureParams = signature.getParameterTypes(); for (int i = 0; i < methodParams.length; i++) { @@ -98,15 +110,24 @@ private boolean isValidMethod(Method method, Method signature) { } /** + * Generates the parser method name for the given key. + * * By default, prepends "parse" and appends "Data" to the key. - * - * @param key - * @return + * + * @param key the key to generate the parser name + * @return the generated parser method name */ public String getParserName(String key) { return "parse" + key + "Data"; } + /** + * Parses data using the method associated with the given key. + * + * @param key the key identifying the parser method + * @param args the arguments to pass to the parser method + * @return the result of the parser method + */ public Object parse(String key, Object... args) { var methodName = getParserName(key); var method = dataParsers.get(methodName); diff --git a/jOptions/src/org/suikasoft/jOptions/values/SetupList.java b/jOptions/src/org/suikasoft/jOptions/values/SetupList.java index 635bba3e..1a26a18c 100644 --- a/jOptions/src/org/suikasoft/jOptions/values/SetupList.java +++ b/jOptions/src/org/suikasoft/jOptions/values/SetupList.java @@ -1,11 +1,11 @@ /* * Copyright 2011 SPeCS Research Group. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. under the License. @@ -30,30 +30,24 @@ import pt.up.fe.specs.util.SpecsLogs; /** - * Represents a list of several SetupData objects. - * + * Represents a list of several SetupData objects, providing methods to manage and access them. + * * @author Joao Bispo */ public class SetupList implements DataStore { - - // Consider replace with LinkedHashMap private final String setupListName; - // private List setupList; private final Map mapOfSetups; - - // Using separate list inst of LinkedHashMap because XStream does not support Arrays.ArrayList, which LinkedHashMap - // uses private final List keys; - - // private Integer preferredIndex; private String preferredSetupName; - // private final SetupOptions helper; - + /** + * Constructs a SetupList with the given name and collection of DataStore objects. + * + * @param setupListName the name of the setup list + * @param listOfSetups the collection of DataStore objects to include in the setup list + */ public SetupList(String setupListName, Collection listOfSetups) { this.setupListName = setupListName; - - // mapOfSetups = SpecsFactory.newLinkedHashMap(); mapOfSetups = new HashMap<>(); keys = new ArrayList<>(); for (DataStore setup : listOfSetups) { @@ -64,216 +58,280 @@ public SetupList(String setupListName, Collection listOfSetups) { + setup.getName() + ")"); } } - preferredSetupName = null; } + /** + * Creates a new SetupList instance with the given name and store definitions. + * + * @param setupListName the name of the setup list + * @param storeDefinitions the store definitions to include in the setup list + * @return a new SetupList instance + */ public static SetupList newInstance(String setupListName, StoreDefinition... storeDefinitions) { return newInstance(setupListName, Arrays.asList(storeDefinitions)); } /** - * Helper method which receives a list of enum classes that implements SetupProvider. - * - * @param setupProvider - * @return + * Creates a new SetupList instance with the given name and list of store definitions. + * + * @param setupListName the name of the setup list + * @param storeDefinitions the list of store definitions to include in the setup list + * @return a new SetupList instance */ - // public static SetupList newInstanceWithEnum(String setupListName, Class... setupProviders) { public static SetupList newInstance(String setupListName, List storeDefinitions) { List listOfSetups = new ArrayList<>(); - // SpecsFactory.newArrayList(); - for (StoreDefinition definition : storeDefinitions) { DataStore aSetup = DataStore.newInstance(definition); listOfSetups.add(aSetup); } - return new SetupList(setupListName, listOfSetups); } - /* - public List getMapOfSetups() { - return setupList; - } - */ - /** - * @return the listOfSetups + * Returns the map of setups. + * + * @return the map of setups */ private Map getMap() { return mapOfSetups; } + /** + * Returns the collection of DataStore objects in this SetupList. + * + * @return the collection of DataStore objects + */ public Collection getDataStores() { return keys.stream() .map(key -> mapOfSetups.get(key)) .collect(Collectors.toList()); } + /** + * Sets the preferred setup by its name. + * + * @param setupName the name of the preferred setup + */ public void setPreferredSetup(String setupName) { - // Check if setup list contains setup name if (!mapOfSetups.containsKey(setupName)) { SpecsLogs .msgInfo("!Tried to set preferred setup of SetupList '" + getSetupListName() + "' to '" + setupName + "', but SetupList does not have that setup. Available setups:" + mapOfSetups.keySet()); } - preferredSetupName = setupName; } + /** + * Returns the preferred setup. + * + * @return the preferred setup, or null if no setups are available + */ public DataStore getPreferredSetup() { if (mapOfSetups.isEmpty()) { SpecsLogs.warn("There are no setups."); return null; } - if (preferredSetupName == null) { - // LoggingUtils.msgWarn("Preferred setup not set, returning first setup."); String firstSetup = getFirstSetup(); return mapOfSetups.get(firstSetup); } - String setupName = mapOfSetups.get(preferredSetupName).getName(); - return mapOfSetups.get(setupName); } /** - * @return + * Returns the name of the first setup in the list. + * + * @return the name of the first setup + * @throws IllegalArgumentException if there are no setups */ private String getFirstSetup() { SpecsCheck.checkArgument(!keys.isEmpty(), () -> "There are no setups!"); return keys.get(0); - // return mapOfSetups.keySet().iterator().next(); } /** - * @return + * Returns the number of setups in this SetupList. + * + * @return the number of setups */ public int getNumSetups() { return keys.size(); } + /** + * Returns the name of the preferred setup. + * + * @return the name of the preferred setup + */ @Override public String getName() { return getPreferredSetup().getName(); } - /* (non-Javadoc) - * @see java.lang.Object#toString() + /** + * Returns a comma-separated string of all setup names in this list. + * + * @return a string representation of the setup list */ @Override public String toString() { StringBuilder builder = new StringBuilder(); for (var key : keys) { - // for (DataStore setup : mapOfSetups.values()) { DataStore setup = mapOfSetups.get(key); if (builder.length() != 0) { builder.append(", "); } - builder.append(setup.getName()); } - return builder.toString(); } /** - * @param class1 - * @return + * Returns the setup with the given name. + * + * @param storeName the name of the setup + * @return the setup with the given name + * @throws RuntimeException if the setup is not found */ public DataStore getSetup(String storeName) { - DataStore setup = mapOfSetups.get(storeName); if (setup == null) { throw new RuntimeException("Could not find setup '" + storeName + "', provided by class '"); } - return setup; } /** - * @return the setupListName + * Returns the name of this setup list. + * + * @return the name of the setup list */ public String getSetupListName() { return setupListName; } + /** + * Returns the DataStore object with the given name. + * + * @param dataStoreName the name of the DataStore object + * @return the DataStore object with the given name, or null if not found + */ public DataStore getDataStore(String dataStoreName) { - // Get inner setup specified by key - - // SimpleSetup innerSetup = setupList.getMap().get(innerKey); DataStore innerSetup = getMap().get(dataStoreName); if (innerSetup == null) { SpecsLogs.msgInfo("SetupList does not contain inner setup '" + dataStoreName + "'. Available setups: " + toString()); return null; } - return innerSetup; } + /** + * Sets a value for the given DataKey in the preferred setup. + * + * @param key the DataKey + * @param value the value to set + * @return this SetupList for chaining + */ @Override - // public Optional set(DataKey key, E value) { public SetupList set(DataKey key, E value) { - // return getPreferredSetup().set(key, value); getPreferredSetup().set(key, value); return this; } + /** + * Sets a raw value for the given key in the preferred setup. + * + * @param key the key + * @param value the value to set + * @return an Optional containing the previous value, if any + */ @Override public Optional setRaw(String key, Object value) { return getPreferredSetup().setRaw(key, value); } - // @Override - // public SetupList set(DataStore dataStore) { - // getPreferredSetup().set(dataStore); - // - // return this; - // } - + /** + * Gets a value for the given DataKey from the preferred setup. + * + * @param key the DataKey + * @return the value associated with the key + */ @Override public T get(DataKey key) { return getPreferredSetup().get(key); } + /** + * Gets a value for the given string key from the preferred setup. + * + * @param id the key + * @return the value associated with the key + */ @Override public Object get(String id) { return getPreferredSetup().get(id); } + /** + * Checks if the preferred setup has a value for the given DataKey. + * + * @param key the DataKey + * @return true if the value is present, false otherwise + */ @Override public boolean hasValue(DataKey key) { return getPreferredSetup().hasValue(key); } + /** + * Sets strict mode for the preferred setup. + * + * @param value true to enable strict mode, false otherwise + */ @Override public void setStrict(boolean value) { getPreferredSetup().setStrict(value); - } - // - // @Override - // public Map getValuesMap() { - // return getPreferredSetup().getValuesMap(); - // } + /** + * Removes the value for the given DataKey from the preferred setup. + * + * @param key the DataKey + * @return an Optional containing the removed value, if any + */ @Override public Optional remove(DataKey key) { return getPreferredSetup().remove(key); } + /** + * Gets the store definition from the preferred setup, if available. + * + * @return an Optional containing the StoreDefinition, if present + */ @Override public Optional getStoreDefinitionTry() { return getPreferredSetup().getStoreDefinitionTry(); } + /** + * Sets the store definition for the preferred setup. + * + * @param definition the StoreDefinition to set + */ @Override public void setStoreDefinition(StoreDefinition definition) { getPreferredSetup().setStoreDefinition(definition); } + /** + * Returns the collection of keys with values in the preferred setup. + * + * @return the collection of keys with values + */ @Override public Collection getKeysWithValues() { return getPreferredSetup().getKeysWithValues(); diff --git a/tdrcLibrary/README.md b/tdrcLibrary/README.md new file mode 100644 index 00000000..a67e2a95 --- /dev/null +++ b/tdrcLibrary/README.md @@ -0,0 +1,15 @@ +# tdrcLibrary + +tdrcLibrary is a Java utility library providing data structures and helper classes for tuples, vectors, and various utility operations such as string, file, and range handling. It is designed to support scientific and engineering applications with reusable components. + +## Features +- Tuple and vector data structures +- String, file, and range utilities +- Serialization helpers +- Collections and mathematical utilities + +## Usage +Add tdrcLibrary to your Java project to use its data structures and utility methods. + +## License +This project is licensed under the Apache License 2.0. diff --git a/tdrcLibrary/src/tdrc/tuple/Triple.java b/tdrcLibrary/src/tdrc/tuple/Triple.java index 63d7748a..8d8f163b 100644 --- a/tdrcLibrary/src/tdrc/tuple/Triple.java +++ b/tdrcLibrary/src/tdrc/tuple/Triple.java @@ -13,48 +13,107 @@ package tdrc.tuple; +/** + * Represents a triple of values. + * + * @param the type of the first value + * @param the type of the second value + * @param the type of the third value + */ public class Triple { private X x; private Y y; private Z z; + /** + * Creates a new instance of Triple with the given values. + * + * @param x the first value + * @param y the second value + * @param z the third value + * @param the type of the first value + * @param the type of the second value + * @param the type of the third value + * @return a new Triple instance + */ public static Triple newInstance(X x, Y y, Z z) { return new Triple<>(x, y, z); } + /** + * Constructs a Triple with the given values. + * + * @param x the first value + * @param y the second value + * @param z the third value + */ protected Triple(X x, Y y, Z z) { this.setX(x); this.setY(y); this.setZ(z); } + /** + * Gets the first value of the Triple. + * + * @return the first value + */ public X getX() { return x; } + /** + * Sets the first value of the Triple. + * + * @param x the first value to set + */ public void setX(X x) { this.x = x; } + /** + * Gets the second value of the Triple. + * + * @return the second value + */ public Y getY() { return y; } + /** + * Sets the second value of the Triple. + * + * @param y the second value to set + */ public void setY(Y y) { this.y = y; } + /** + * Gets the third value of the Triple. + * + * @return the third value + */ public Z getZ() { return z; } + /** + * Sets the third value of the Triple. + * + * @param z the third value to set + */ public void setZ(Z z) { this.z = z; } + /** + * Returns a string representation of the Triple. + * + * @return a string in the format "(x,y,z)" + */ @Override public String toString() { - return "(" + x + "," + y + "," + z + ")"; } diff --git a/tdrcLibrary/src/tdrc/tuple/Tuple.java b/tdrcLibrary/src/tdrc/tuple/Tuple.java index 77d7d98b..9e1a0573 100644 --- a/tdrcLibrary/src/tdrc/tuple/Tuple.java +++ b/tdrcLibrary/src/tdrc/tuple/Tuple.java @@ -21,61 +21,129 @@ import org.apache.commons.lang3.builder.CompareToBuilder; +/** + * Represents a tuple of values. + * + * @param the type of the elements in the tuple + */ public class Tuple extends AbstractList implements Comparable> { - List tuple; - BiFunction, Tuple, Integer> comparator; + private List tuple; + private BiFunction, Tuple, Integer> comparator; private BiFunction, Tuple, Double> distanceCalculator; + /** + * Creates a new instance of an empty tuple. + * + * @param the type of the elements in the tuple + * @return a new empty tuple + */ public static Tuple newInstance() { return new Tuple<>(); } + /** + * Creates a new instance of a tuple with the given elements. + * + * @param the type of the elements in the tuple + * @param elements the elements to include in the tuple + * @return a new tuple containing the given elements + */ public static Tuple newInstance(List elements) { return new Tuple<>(elements); } + /** + * Creates a new instance of a tuple with the given elements. + * + * @param the type of the elements in the tuple + * @param elements the elements to include in the tuple + * @return a new tuple containing the given elements + */ @SafeVarargs public static Tuple newInstance(T... elements) { return new Tuple<>(Arrays.asList(elements)); } + /** + * Constructs a tuple with the given elements. + * + * @param elements the elements to include in the tuple + */ private Tuple(List elements) { tuple = new ArrayList<>(elements); this.comparator = Tuple::defaultComparator; this.distanceCalculator = Tuple::defaultDistanceCalculator; } + /** + * Constructs an empty tuple. + */ private Tuple() { tuple = new ArrayList<>(); } + /** + * Retrieves the element at the specified index. + * + * @param index the index of the element to retrieve + * @return the element at the specified index + */ @Override public T get(int index) { return tuple.get(index); } + /** + * Returns the number of elements in the tuple. + * + * @return the size of the tuple + */ @Override public int size() { - return tuple.size(); } + /** + * Replaces the element at the specified index with the given element. + * + * @param index the index of the element to replace + * @param element the new element + * @return the previous element at the specified index + */ @Override public T set(int index, T element) { return tuple.set(index, element); } + /** + * Adds an element at the specified index. + * + * @param index the index at which to add the element + * @param element the element to add + */ @Override public void add(int index, T element) { tuple.add(index, element); } + /** + * Removes the specified element from the tuple. + * + * @param o the element to remove + * @return true if the element was removed, false otherwise + */ @Override public boolean remove(Object o) { return tuple.remove(o); } + /** + * Checks if this tuple is equal to another object. + * + * @param o the object to compare + * @return true if the object is a tuple and has the same elements, false otherwise + */ @Override public boolean equals(Object o) { if (!(o instanceof Tuple)) { @@ -94,33 +162,25 @@ public boolean equals(Object o) { return true; } + /** + * Compares this tuple to another tuple. + * + * @param o the tuple to compare + * @return a negative integer, zero, or a positive integer as this tuple is less than, equal to, or greater than the specified tuple + */ @Override public int compareTo(Tuple o) { return comparator.apply(this, o); } - /*@SuppressWarnings({ "rawtypes", "unchecked" }) - public int compareTo(final Tuple o) { - - final int tLen = this.valueArray.length; - final Object[] oValues = o.valueArray; - final int oLen = oValues.length; - - for (int i = 0; i < tLen && i < oLen; i++) { - - final Comparable tElement = (Comparable)this.valueArray[i]; - final Comparable oElement = (Comparable)oValues[i]; - - final int comparison = tElement.compareTo(oElement); - if (comparison != 0) { - return comparison; - } - - } - - return (Integer.valueOf(tLen)).compareTo(Integer.valueOf(oLen)); - - }*/ + /** + * Default comparator for tuples. + * + * @param tuple the first tuple + * @param tuple2 the second tuple + * @param the type of the elements in the tuples + * @return a negative integer, zero, or a positive integer as the first tuple is less than, equal to, or greater than the second tuple + */ public static Integer defaultComparator(Tuple tuple, Tuple tuple2) { CompareToBuilder compareToBuilder = new CompareToBuilder(); final int tLen = tuple.size(); @@ -128,31 +188,28 @@ public static Integer defaultComparator(Tuple tuple, Tuple tuple2) { for (int i = 0; i < tLen && i < oLen; i++) { compareToBuilder.append(tuple.get(i), tuple2.get(i)); } - return compareToBuilder.toComparison(); - - // final int tLen = tuple.size(); - // final int oLen = tuple2.size(); - // - // for (int i = 0; i < tLen && i < oLen; i++) { - // - // final Comparable tElement = (Comparable) tuple.get(i); - // // final Comparable oElement = (Comparable) tuple2.get(i); - // - // final int comparison = tElement.compareTo(tuple2.get(i)); - // if (comparison != 0) { - // return comparison; - // } - // } - // return tLen - oLen; } + /** + * Calculates the distance between this tuple and another tuple. + * + * @param otherTuple the other tuple + * @return the distance between the tuples + */ public double getDistance(Tuple otherTuple) { return distanceCalculator.apply(this, otherTuple); } + /** + * Default distance calculator for tuples. + * + * @param tuple the first tuple + * @param tuple2 the second tuple + * @param the type of the elements in the tuples + * @return the distance between the tuples + */ public static Double defaultDistanceCalculator(Tuple tuple, Tuple tuple2) { - final int tLen = tuple.size(); final int oLen = tuple2.size(); double sum = 0; @@ -165,29 +222,23 @@ public static Double defaultDistanceCalculator(Tuple tuple, Tuple tupl double tD = ((Number) t).doubleValue() - ((Number) t2).doubleValue(); sum += tD * tD; } - return Math.sqrt(sum); - - // final int tLen = tuple.size(); - // final int oLen = tuple2.size(); - // - // for (int i = 0; i < tLen && i < oLen; i++) { - // - // final Comparable tElement = (Comparable) tuple.get(i); - // // final Comparable oElement = (Comparable) tuple2.get(i); - // - // final int comparison = tElement.compareTo(tuple2.get(i)); - // if (comparison != 0) { - // return comparison; - // } - // } - // return tLen - oLen; } + /** + * Retrieves the distance calculator for this tuple. + * + * @return the distance calculator + */ public BiFunction, Tuple, Double> getDistanceCalculator() { return distanceCalculator; } + /** + * Sets the distance calculator for this tuple. + * + * @param distanceCalculator the new distance calculator + */ public void setDistanceCalculator(BiFunction, Tuple, Double> distanceCalculator) { this.distanceCalculator = distanceCalculator; } diff --git a/tdrcLibrary/src/tdrc/tuple/TupleList.java b/tdrcLibrary/src/tdrc/tuple/TupleList.java index ad1abcd9..a0ad0878 100644 --- a/tdrcLibrary/src/tdrc/tuple/TupleList.java +++ b/tdrcLibrary/src/tdrc/tuple/TupleList.java @@ -19,20 +19,37 @@ import java.util.List; /** - * This class is an encapsulation of List>. Does not garantee that each tuple has to be of the same length + * Represents a list of Tuple objects, providing utility methods for tuple management. + * This class is an encapsulation of List>. Does not guarantee that each tuple has to be of the same length. * * @author tiago * - * @param + * @param the type of elements in the tuples */ public class TupleList extends AbstractList> { + /** + * The list of tuples managed by this class. + */ List> tuples; + /** + * Creates a new instance of TupleList. + * + * @param the type of elements in the tuples + * @return a new instance of TupleList + */ public static TupleList newInstance() { return new TupleList<>(); } + /** + * Creates a new instance of TupleList from multiple lists of elements. + * + * @param the type of elements in the tuples + * @param tuples the lists of elements to be converted into tuples + * @return a new instance of TupleList containing the provided tuples + */ @SafeVarargs public static TupleList newInstance(List... tuples) { TupleList tupleList = new TupleList<>(); @@ -42,6 +59,13 @@ public static TupleList newInstance(List... tuples) { return tupleList; } + /** + * Creates a new instance of TupleList from multiple arrays of elements. + * + * @param the type of elements in the tuples + * @param tuples the arrays of elements to be converted into tuples + * @return a new instance of TupleList containing the provided tuples + */ @SafeVarargs public static TupleList newInstance(T[]... tuples) { TupleList tupleList = new TupleList<>(); @@ -51,43 +75,84 @@ public static TupleList newInstance(T[]... tuples) { return tupleList; } + /** + * Private constructor to initialize the list of tuples. + */ private TupleList() { tuples = new ArrayList<>(); } + /** + * Retrieves the tuple at the specified index. + * + * @param index the index of the tuple to retrieve + * @return the tuple at the specified index + */ @Override public Tuple get(int index) { return tuples.get(index); } + /** + * Returns the number of tuples in the list. + * + * @return the size of the tuple list + */ @Override public int size() { - return tuples.size(); } + /** + * Replaces the tuple at the specified index with the provided tuple. + * + * @param index the index of the tuple to replace + * @param tuple the new tuple to set at the specified index + * @return the previous tuple at the specified index + */ @Override public Tuple set(int index, Tuple tuple) { return tuples.set(index, tuple); } + /** + * Adds a new tuple at the specified index, created from the provided list of elements. + * + * @param index the index at which to add the new tuple + * @param elements the list of elements to create the new tuple + */ public void add(int index, List elements) { tuples.add(index, Tuple.newInstance(elements)); } + /** + * Adds a new tuple to the end of the list, created from the provided list of elements. + * + * @param elements the list of elements to create the new tuple + */ public void add(List elements) { tuples.add(Tuple.newInstance(elements)); } + /** + * Adds a new tuple at the specified index. + * + * @param index the index at which to add the new tuple + * @param tuple the tuple to add + */ @Override public void add(int index, Tuple tuple) { - tuples.add(index, tuple); } + /** + * Removes the specified tuple from the list. + * + * @param o the tuple to remove + * @return true if the tuple was successfully removed, false otherwise + */ @Override public boolean remove(Object o) { - // TODO Auto-generated method stub return tuples.remove(o); } diff --git a/tdrcLibrary/src/tdrc/tuple/TupleUtils.java b/tdrcLibrary/src/tdrc/tuple/TupleUtils.java index 28763a61..9da15fe5 100644 --- a/tdrcLibrary/src/tdrc/tuple/TupleUtils.java +++ b/tdrcLibrary/src/tdrc/tuple/TupleUtils.java @@ -23,8 +23,20 @@ import tdrc.utils.Pair; +/** + * Utility class for operations on Tuple objects. + */ public class TupleUtils { + /** + * Creates a normalized map from a collection of tuples. Each tuple is normalized based on the maximum and minimum + * values of its elements. + * + * @param the type of number in the tuple + * @param tuples the collection of tuples to normalize + * @param tupleSize the expected size of each tuple + * @return a map where the keys are the original tuples and the values are the normalized tuples + */ public static Map, Tuple> createNormalizedMap(Collection> tuples, int tupleSize) { List maxs = new ArrayList<>(tupleSize); @@ -73,6 +85,12 @@ public static Map, Tuple> createNormalizedMap return normalizedMap; } + /** + * Calculates the Euclidean distances between all pairs of tuples in the given collection. + * + * @param tuples the collection of tuples + * @return a map where the keys are tuples and the values are maps of tuples to their Euclidean distances + */ public static Map, Map, Float>> eucledianDistances(Collection> tuples) { Map, Map, Float>> eucledianMap = new HashMap<>(); @@ -87,6 +105,13 @@ public static Map, Map, Float>> eucledianDistances(Col return eucledianMap; } + /** + * Calculates the Euclidean distance between two tuples. + * + * @param tuple the first tuple + * @param tuple2 the second tuple + * @return the Euclidean distance between the two tuples + */ public static double getDistance(Tuple tuple, Tuple tuple2) { double sum = 0; for (int i = 0; i < tuple.size(); i++) { @@ -96,6 +121,14 @@ public static double getDistance(Tuple tuple, Tuple, List, Float>>> eucledianDistancesByClosest( Collection> tuples) { diff --git a/tdrcLibrary/src/tdrc/utils/FileUtils.java b/tdrcLibrary/src/tdrc/utils/FileUtils.java index f790f71e..c071141f 100644 --- a/tdrcLibrary/src/tdrc/utils/FileUtils.java +++ b/tdrcLibrary/src/tdrc/utils/FileUtils.java @@ -19,11 +19,14 @@ import pt.up.fe.specs.util.SpecsIo; +/** + * Utility class for file operations in tdrcLibrary. + */ public class FileUtils { /** * Retrieve a list of files if the files match to the given extension - * (accepts regular expressions) + * (accepts regular expressions). * * @param dir * the directory to search on @@ -32,6 +35,8 @@ public class FileUtils { * @param recursive * should the search be recursive on inner folders? * @return a list of accepted files (directories not included!) + * @throws RuntimeException + * if the given file is not a folder */ public static List getFilesFromDir(File dir, String extension, boolean recursive) { final List filesList = new ArrayList<>(); @@ -44,19 +49,21 @@ public static List getFilesFromDir(File dir, String extension, boolean rec } /** - * Auxiliary method for - * {@link FileUtils#getFilesFromDir(File, String, boolean)} + * Auxiliary method for {@link FileUtils#getFilesFromDir(File, String, boolean)}. * * @param dir + * the directory to search on * @param extension + * the regular expression of the accepted extensions * @param recursive + * should the search be recursive on inner folders? * @param files + * the list to store the accepted files */ private static void addFilesFromDir(File dir, String extension, boolean recursive, List files) { final List folders = new ArrayList<>(); for (final File f : dir.listFiles()) { - if (f.isDirectory() && recursive) { // Necessary to give priority to - // files in current directory + if (f.isDirectory() && recursive) { // Necessary to give priority to files in current directory folders.add(f); } else if (SpecsIo.getExtension(f).matches(extension)) { files.add(f); diff --git a/tdrcLibrary/src/tdrc/utils/HashBag.java b/tdrcLibrary/src/tdrc/utils/HashBag.java index 5455faf8..1e725352 100644 --- a/tdrcLibrary/src/tdrc/utils/HashBag.java +++ b/tdrcLibrary/src/tdrc/utils/HashBag.java @@ -19,39 +19,39 @@ import java.util.Set; /** + * A bag (multiset) implementation backed by a hash map. * * @author Tiago * - * @param + * @param the type of elements in the bag */ public class HashBag { private final Map bag; + /** + * Constructs an empty HashBag. + */ public HashBag() { - this.bag = new HashMap<>(); } /** - * Puts one occurrence of element T and returns the total number of occurrences + * Adds one occurrence of the specified element to the bag and returns the total number of occurrences. * - * @param element - * the element to add an occurrence - * @return total number of occurrences of the element + * @param element the element to add + * @return the total number of occurrences of the element */ public int put(T element) { return put(element, 1); } /** - * Puts a number of occurrences of element T and returns the total number of occurrences + * Adds the specified number of occurrences of the element to the bag and returns the total number of occurrences. * - * @param element - * the element to add occurrences - * @param val - * the number of occurrences to set - * @return total number of occurrences of the element + * @param element the element to add + * @param val the number of occurrences to add + * @return the total number of occurrences of the element */ public int put(T element, int val) { final int newVal = get(element) + val; @@ -60,10 +60,10 @@ public int put(T element, int val) { } /** - * Get the number of occurrences of element T + * Returns the number of occurrences of the specified element in the bag. * - * @param Element - * @return + * @param element the element to query + * @return the number of occurrences of the element */ public int get(T element) { final Integer occur = this.bag.get(element); @@ -74,24 +74,21 @@ public int get(T element) { } /** - * Takes one occurrence of element T and returns the remaining number of occurrences + * Removes one occurrence of the specified element from the bag and returns the remaining number of occurrences. * - * @param element - * the element to add an occurrence - * @return remaining number of occurrences of the element + * @param element the element to remove + * @return the remaining number of occurrences of the element */ public int take(T element) { return take(element, 1); } /** - * Takes a number of occurrences of element T and returns the remaining number of occurrences + * Removes the specified number of occurrences of the element from the bag and returns the remaining number of occurrences. * - * @param element - * the element to add an occurrence - * @param val - * the number of occurrences to take; - * @return remaining number of occurrences of the element + * @param element the element to remove + * @param val the number of occurrences to remove + * @return the remaining number of occurrences of the element */ public int take(T element, int val) { final int newVal = get(element) - val; @@ -106,51 +103,60 @@ public int take(T element, int val) { } /** - * Return a set containing the keys on this bag + * Returns a set containing all the keys (elements) in the bag. * - * @return + * @return a set of keys in the bag */ public Set keySet() { return this.bag.keySet(); } + /** + * Returns a string representation of the bag. + * + * @return a string representation of the bag + */ @Override public String toString() { final StringBuilder sb = new StringBuilder("{"); this.bag.keySet().stream().forEach(k -> sb.append(k + "=" + this.bag.get(k) + ", ")); - sb.append("}"); return sb.toString(); } /** - * Remove all itens from the bag
    - * NOTE: actual removal of items, if you intend to reset contents use {@link HashBag#reset()} + * Removes all items from the bag. + *
    + * NOTE: This method performs actual removal of items. If you intend to reset contents, use {@link HashBag#reset()}. */ public void clear() { bag.clear(); } /** - * Resets the items counters to zero
    - * NOTE: does not remove the items, just resets to zeros, if you intend to remove the items use - * {@link HashBag#clear()} + * Resets the item counters to zero without removing the items. + *
    + * NOTE: This method does not remove the items, just resets their counts to zero. If you intend to remove the items, use {@link HashBag#clear()}. */ public void reset() { bag.replaceAll((k, v) -> 0); } /** - * Returns the total number of itens in the bag + * Returns the total number of items in the bag. * - * @return + * @return the total number of items in the bag */ public int getTotal() { return bag.values().stream().reduce((a, b) -> a + b).orElse(0); } + /** + * Returns an unmodifiable view of the bag as a map. + * + * @return an unmodifiable map view of the bag + */ public Map toMap() { - return Collections.unmodifiableMap(bag); } } diff --git a/tdrcLibrary/src/tdrc/utils/ListUtils.java b/tdrcLibrary/src/tdrc/utils/ListUtils.java index f9ea58b2..fdde5f5f 100644 --- a/tdrcLibrary/src/tdrc/utils/ListUtils.java +++ b/tdrcLibrary/src/tdrc/utils/ListUtils.java @@ -21,12 +21,16 @@ import tdrc.tuple.Triple; import tdrc.tuple.TupleList; +/** + * Utility class for list operations in tdrcLibrary. + */ public class ListUtils { /** - * Combine N arrays to create a list of N-tuples + * Combine N arrays to create a list of N-tuples. * - * @param tuples - * @return + * @param arraysToCombine Arrays to be combined into tuples. + * @param The type of elements in the arrays. + * @return A TupleList containing the combined tuples. */ @SafeVarargs public static TupleList createTuples(T[]... arraysToCombine) { @@ -34,10 +38,11 @@ public static TupleList createTuples(T[]... arraysToCombine) { } /** - * Combine N lists to create a list of N-tuples + * Combine N lists to create a list of N-tuples. * - * @param tuples - * @return + * @param arraysToCombine Lists to be combined into tuples. + * @param The type of elements in the lists. + * @return A TupleList containing the combined tuples. */ public static TupleList createTuples(List arraysToCombine) { TupleList tuples = TupleList.newInstance(); @@ -47,6 +52,16 @@ public static TupleList createTuples(List arraysToCombine) { return tuples; } + /** + * Helper method for creating tuples from arrays. + * + * @param arraysToCombine Lists to be combined into tuples. + * @param position Current position in the recursion. + * @param tuple Current tuple being constructed. + * @param tuples TupleList to store the resulting tuples. + * @param tupleSize Size of the tuples. + * @param The type of elements in the lists. + */ private static void tuplesAux(List arraysToCombine, int position, List tuple, TupleList tuples, int tupleSize) { if (position >= tupleSize) { @@ -62,6 +77,13 @@ private static void tuplesAux(List arraysToCombine, int position, List< } } + /** + * Combine lists to create a list of tuples. + * + * @param arraysToCombine Lists to be combined into tuples. + * @param The type of elements in the lists. + * @return A TupleList containing the combined tuples. + */ public static TupleList createTuplesFromList(List> arraysToCombine) { TupleList tuples = TupleList.newInstance(); int tupleSize = arraysToCombine.size(); @@ -70,6 +92,13 @@ public static TupleList createTuplesFromList(List> arraysToCombin return tuples; } + /** + * Combine lists to create a list of tuples. + * + * @param arraysToCombine Lists to be combined into tuples. + * @param The type of elements in the lists. + * @return A TupleList containing the combined tuples. + */ @SafeVarargs public static TupleList createTuplesFromList(List... arraysToCombine) { TupleList tuples = TupleList.newInstance(); @@ -79,6 +108,16 @@ public static TupleList createTuplesFromList(List... arraysToCombine) return tuples; } + /** + * Helper method for creating tuples from lists. + * + * @param arraysToCombine Lists to be combined into tuples. + * @param position Current position in the recursion. + * @param tuple Current tuple being constructed. + * @param tuples TupleList to store the resulting tuples. + * @param tupleSize Size of the tuples. + * @param The type of elements in the lists. + */ private static void tuplesFromListAux(List> arraysToCombine, int position, List tuple, TupleList tuples, int tupleSize) { @@ -96,11 +135,13 @@ private static void tuplesFromListAux(List> arraysToCombine, int pos } /** - * Combine two arrays to create a list of pairs + * Combine two arrays to create a list of pairs. * - * @param left - * @param right - * @return + * @param left The first array. + * @param right The second array. + * @param The type of elements in the first array. + * @param The type of elements in the second array. + * @return A list of pairs combining elements from both arrays. */ public static List> createPairs(T[] left, V[] right) { @@ -114,11 +155,13 @@ public static List> createPairs(T[] left, V[] right) { } /** - * Combine two lists to create a list of pairs + * Combine two lists to create a list of pairs. * - * @param left - * @param right - * @return + * @param left The first list. + * @param right The second list. + * @param The type of elements in the first list. + * @param The type of elements in the second list. + * @return A list of pairs combining elements from both lists. */ public static List> createPairs(List left, List right) { @@ -128,12 +171,15 @@ public static List> createPairs(List left, List right) { } /** - * Combine three arrays to create a list of triples + * Combine three arrays to create a list of triples. * - * @param xs - * @param ys - * @param zs - * @return + * @param xs The first array. + * @param ys The second array. + * @param zs The third array. + * @param The type of elements in the first array. + * @param The type of elements in the second array. + * @param The type of elements in the third array. + * @return A list of triples combining elements from all three arrays. */ public static List> createTriples(X[] xs, Y[] ys, Z[] zs) { @@ -149,12 +195,15 @@ public static List> createTriples(X[] xs, Y[] ys, Z[] } /** - * Combine three lists to create a list of triples + * Combine three lists to create a list of triples. * - * @param xs - * @param ys - * @param zs - * @return + * @param xs The first list. + * @param ys The second list. + * @param zs The third list. + * @param The type of elements in the first list. + * @param The type of elements in the second list. + * @param The type of elements in the third list. + * @return A list of triples combining elements from all three lists. */ public static List> createTriples(List xs, List ys, List zs) { diff --git a/tdrcLibrary/src/tdrc/utils/Pair.java b/tdrcLibrary/src/tdrc/utils/Pair.java index e9d37ba8..a500e854 100644 --- a/tdrcLibrary/src/tdrc/utils/Pair.java +++ b/tdrcLibrary/src/tdrc/utils/Pair.java @@ -14,64 +14,99 @@ package tdrc.utils; /** - * @author Tiago + * Represents a pair of values. * + * @param the type of the first value + * @param the type of the second value */ public class Pair { private K left; private V right; + /** + * Creates a new instance of Pair with the specified values. + * + * @param the type of the first value + * @param the type of the second value + * @param left the first value + * @param right the second value + * @return a new Pair instance + */ public static Pair newInstance(K left, V right) { - return new Pair<>(left, right); } + /** + * Default constructor. Initializes the pair with null values. + */ public Pair() { this.left = null; this.right = null; } + /** + * Constructor that initializes the pair with the specified values. + * + * @param left the first value + * @param right the second value + */ public Pair(K left, V right) { setLeft(left); setRight(right); } + /** + * Returns a string representation of the pair. + * + * @return a string in the format "(left, right)" + */ @Override public String toString() { return "(" + this.left + ", " + this.right + ")"; } /** - * @return the left + * Gets the first value of the pair. + * + * @return the first value */ public K getLeft() { return this.left; } /** - * @param left - * the left to set + * Sets the first value of the pair. + * + * @param left the value to set */ public void setLeft(K left) { this.left = left; } /** - * @return the right + * Gets the second value of the pair. + * + * @return the second value */ public V getRight() { return this.right; } /** - * @param right - * the right to set + * Sets the second value of the pair. + * + * @param right the value to set */ public void setRight(V right) { this.right = right; } + /** + * Computes the hash code for the pair. + * + * @return the hash code + */ @Override public int hashCode() { final int prime = 31; @@ -81,6 +116,12 @@ public int hashCode() { return result; } + /** + * Checks if this pair is equal to another object. + * + * @param obj the object to compare + * @return true if the object is a pair with equal values, false otherwise + */ @Override public boolean equals(Object obj) { if (this == obj) { @@ -109,12 +150,4 @@ public boolean equals(Object obj) { } return true; } - - /* - * @Override public boolean equals(Object obj) { if (!(obj instanceof Pair)) - * return false; Pair otherPair = (Pair) obj; return - * this.getLeft().equals(otherPair.getLeft()) && - * this.getRight().equals(otherPair.getRight()); } - */ - } diff --git a/tdrcLibrary/src/tdrc/utils/PairList.java b/tdrcLibrary/src/tdrc/utils/PairList.java index 8bb56960..6e2de526 100644 --- a/tdrcLibrary/src/tdrc/utils/PairList.java +++ b/tdrcLibrary/src/tdrc/utils/PairList.java @@ -15,19 +15,25 @@ import java.util.ArrayList; +/** + * Represents a list of Pair objects. + * + * @param the type of the key in the pair + * @param the type of the value in the pair + */ public class PairList extends ArrayList> { /** - * + * Serial version UID for serialization. */ private static final long serialVersionUID = 327775886389736L; /** - * Create and add a new Pair to the list + * Creates and adds a new Pair to the list. * - * @param left - * @param right - * @return the new pair + * @param left the key of the pair + * @param right the value of the pair + * @return the newly created pair, or null if the pair could not be added */ public Pair add(K left, V right) { final Pair pair = new Pair<>(left, right); @@ -38,11 +44,10 @@ public Pair add(K left, V right) { } /** - * Get the last Pair in the list + * Retrieves the last Pair in the list. * - * @return - * @throws IndexOutOfBoundsException - * if the list is empty + * @return the last pair in the list + * @throws IndexOutOfBoundsException if the list is empty */ public Pair last() { if (isEmpty()) { @@ -53,11 +58,10 @@ public Pair last() { } /** - * Get the first Pair in the list + * Retrieves the first Pair in the list. * - * @return - * @throws IndexOutOfBoundsException - * if the list is empty + * @return the first pair in the list + * @throws IndexOutOfBoundsException if the list is empty */ public Pair first() { if (isEmpty()) { diff --git a/tdrcLibrary/src/tdrc/utils/RangeMap.java b/tdrcLibrary/src/tdrc/utils/RangeMap.java index 7de3f306..9c7742c4 100644 --- a/tdrcLibrary/src/tdrc/utils/RangeMap.java +++ b/tdrcLibrary/src/tdrc/utils/RangeMap.java @@ -8,7 +8,7 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package tdrc.utils; @@ -17,83 +17,108 @@ import java.util.TreeMap; /** - * see: - * http://stackoverflow.com/questions/13399821/data-structures-that-can-map-a- - * range-of-values-to-a-key + * Represents a map of ranges to values. * - * Assumes non-overlapping ranges. Deal with it! + * Assumes non-overlapping ranges. * * @author --- * - * @param - * @param + * @param the type of the range bounds, must extend Number + * @param the type of the values associated with ranges */ public class RangeMap { - private final TreeMap map; - - public RangeMap() { - - map = new TreeMap<>(); - } - - public V get(K key) { - - Entry e = getLowerEntry(key); - - return e == null ? null : e.getValue(); - } - - private Entry getLowerEntry(K key) { - - Entry e = map.floorEntry(key); - if (e != null && e.getValue() == null) { - e = map.lowerEntry(key); - } - return e; - } - - public void put(K lower, K upper, V value) { - - map.put(lower, value); - map.put(upper, null); - } - - public void clear() { - - map.clear(); - } - - /** - * - * @param lower - * -- the lower bound of the range - */ - public V remove(K lower) { - - Entry lowerEntry = getLowerEntry(lower); - - if (lowerEntry == null || lowerEntry.getValue() == null) { - return null; - } - - Entry upperEntry = map.higherEntry(lower); - - if (upperEntry == null) { - return null; - } - - map.remove(upperEntry.getKey()); - return map.remove(lowerEntry.getKey()); - } - - public int size() { - - return map.size() / 2; - } - - int elements() { - - return map.size(); - } + private final TreeMap map; + + /** + * Constructs an empty RangeMap. + */ + public RangeMap() { + map = new TreeMap<>(); + } + + /** + * Retrieves the value associated with the range containing the given key. + * + * @param key the key to search for + * @return the value associated with the range containing the key, or null if no such range exists + */ + public V get(K key) { + Entry e = getLowerEntry(key); + return e == null ? null : e.getValue(); + } + + /** + * Retrieves the entry with the largest key less than or equal to the given key. + * + * @param key the key to search for + * @return the entry with the largest key less than or equal to the given key, or null if no such entry exists + */ + private Entry getLowerEntry(K key) { + Entry e = map.floorEntry(key); + if (e != null && e.getValue() == null) { + e = map.lowerEntry(key); + } + return e; + } + + /** + * Adds a range to the map. + * + * @param lower the lower bound of the range + * @param upper the upper bound of the range + * @param value the value to associate with the range + */ + public void put(K lower, K upper, V value) { + map.put(lower, value); + map.put(upper, null); + } + + /** + * Removes all ranges from the map. + */ + public void clear() { + map.clear(); + } + + /** + * Removes the range starting at the given lower bound. + * + * @param lower the lower bound of the range to remove + * @return the value associated with the removed range, or null if no such range exists + */ + public V remove(K lower) { + Entry lowerEntry = getLowerEntry(lower); + + if (lowerEntry == null || lowerEntry.getValue() == null) { + return null; + } + + Entry upperEntry = map.higherEntry(lower); + + if (upperEntry == null) { + return null; + } + + map.remove(upperEntry.getKey()); + return map.remove(lowerEntry.getKey()); + } + + /** + * Returns the number of ranges in the map. + * + * @return the number of ranges in the map + */ + public int size() { + return map.size() / 2; + } + + /** + * Returns the total number of elements in the map. + * + * @return the total number of elements in the map + */ + int elements() { + return map.size(); + } } diff --git a/tdrcLibrary/src/tdrc/utils/RangeUtils.java b/tdrcLibrary/src/tdrc/utils/RangeUtils.java index 16c4c595..55c25b32 100644 --- a/tdrcLibrary/src/tdrc/utils/RangeUtils.java +++ b/tdrcLibrary/src/tdrc/utils/RangeUtils.java @@ -16,36 +16,28 @@ import java.util.Map.Entry; import java.util.TreeMap; +/** + * Utility class for range operations in tdrcLibrary. + */ public class RangeUtils { /** - * Example of usage:
    - * TreeMap m = new TreeMap();
    - * m.put(1.0, 'A');
    - * m.put(2.9, null);
    - * m.put(4.0, 'B');
    - * m.put(6.0, null);
    - * m.put(6.5, 'C');
    - * m.put(10.0, null);
    - * getValueByRangedKey(m, 5) == 'B'
    - *
    - * More results:
    - * 0.9 null
    - * 1.0 A
    - * 1.1 A
    - * 2.8 A
    - * 2.9 A
    - * 3.0 null
    - * 6.4 null
    - * 6.5 C
    - * 6.6 C
    - * 9.9 C
    - * 10.0 C
    - * 10.1 null
    + * Retrieves the value associated with the closest key less than or equal to the given key in the provided TreeMap. + * If the closest key's value is null, it continues searching for the next lower key. + * + * Example of usage: + * TreeMap m = new TreeMap(); + * m.put(1.0, 'A'); + * m.put(2.9, null); + * m.put(4.0, 'B'); + * m.put(6.0, null); + * m.put(6.5, 'C'); + * m.put(10.0, null); + * getValueByRangedKey(m, 5) == 'B' * - * @param map - * @param key - * @return + * @param map the TreeMap containing the key-value pairs + * @param key the key to search for + * @return the value associated with the closest key less than or equal to the given key, or null if no such key exists */ public static V getValueByRangedKey(TreeMap map, K key) { Entry e = map.floorEntry(key); diff --git a/tdrcLibrary/src/tdrc/utils/SerializeUtils.java b/tdrcLibrary/src/tdrc/utils/SerializeUtils.java index 04d72eec..c211527f 100644 --- a/tdrcLibrary/src/tdrc/utils/SerializeUtils.java +++ b/tdrcLibrary/src/tdrc/utils/SerializeUtils.java @@ -13,7 +13,6 @@ package tdrc.utils; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; @@ -21,14 +20,16 @@ import java.io.OutputStream; import java.io.Serializable; +/** + * Utility class for serialization operations in tdrcLibrary. + */ public class SerializeUtils { /** - * Export a serializable object to a given output stream + * Exports a serializable object to a given output stream. * - * @param obj - * @param outStream - * @throws FileNotFoundException - * @throws IOException + * @param obj the object to be serialized + * @param outStream the output stream where the object will be written + * @throws RuntimeException if a problem occurs during serialization */ public static void toStream(T obj, OutputStream outStream) { @@ -42,21 +43,19 @@ public static void toStream(T obj, OutputStream outStre } /** - * Import a serializable object from a given input stream + * Imports a serializable object from a given input stream. * - * @param obj - * @param outStream - * @throws FileNotFoundException - * @throws IOException + * @param inputStream the input stream from which the object will be read + * @param targetClass the class type of the object to be deserialized + * @return the deserialized object + * @throws RuntimeException if a problem occurs during deserialization */ public static T fromStream(InputStream inputStream, Class targetClass) { - // Write object with ObjectOutputStream + // Read object with ObjectInputStream try (ObjectInputStream obj_in = new ObjectInputStream(inputStream)) { - // Write object out to disk - + // Read object from stream return targetClass.cast(obj_in.readObject()); - } catch (IOException | ClassNotFoundException e) { throw new RuntimeException("Problem during deserialization.", e); } diff --git a/tdrcLibrary/src/tdrc/utils/StringUtils.java b/tdrcLibrary/src/tdrc/utils/StringUtils.java index 21a1df0d..34ba3f3c 100644 --- a/tdrcLibrary/src/tdrc/utils/StringUtils.java +++ b/tdrcLibrary/src/tdrc/utils/StringUtils.java @@ -30,6 +30,12 @@ import org.w3c.dom.Document; +/** + * Utility class for string operations in tdrcLibrary. + *

    + * This class provides various utility methods for string manipulation, including sanitization, case conversion, + * joining strings, package comparison, and XML conversion. + */ public class StringUtils { static final String keywordPrefix = "_"; @@ -41,20 +47,20 @@ public class StringUtils { "volatile", "while" }; /** - * Verifies if a given String is a reserved keyword of Java + * Verifies if a given String is a reserved keyword of Java. * - * @param keyword - * @return + * @param keyword the string to check + * @return true if the string is a reserved keyword, false otherwise */ public static boolean isJavaKeyword(String keyword) { return (Arrays.binarySearch(StringUtils.keywords, keyword) >= 0); } /** - * Returns a sanitized string for the given name, i.e., insures that the name is not a reserved keyword + * Returns a sanitized string for the given name, i.e., ensures that the name is not a reserved keyword. * - * @param name - * @return + * @param name the string to sanitize + * @return the sanitized string */ public static String getSanitizedName(String name) { if (isJavaKeyword(name)) { @@ -63,22 +69,33 @@ public static String getSanitizedName(String name) { return name; } + /** + * Converts the first character of the given string to uppercase. + * + * @param string the input string + * @return the string with the first character converted to uppercase + */ public static String firstCharToUpper(String string) { return charToUpperOrLower(string, 0, true); } + /** + * Converts the first character of the given string to lowercase. + * + * @param string the input string + * @return the string with the first character converted to lowercase + */ public static String firstCharToLower(String string) { return charToUpperOrLower(string, 0, false); } /** - * Puts the given char to upper (true) or lower (false) case + * Converts the character at the specified position in the given string to upper or lower case. * - * @param string - * @param pos - * @param upper - * if set to true, the char is set to upper case; if set to false, the char is set to lower case - * @return + * @param string the input string + * @param pos the position of the character to convert + * @param upper if true, converts the character to uppercase; if false, converts to lowercase + * @return the string with the character at the specified position converted */ public static String charToUpperOrLower(String string, int pos, boolean upper) { if (pos < 0 || pos >= string.length()) { @@ -94,18 +111,25 @@ public static String charToUpperOrLower(String string, int pos, boolean upper) { return ret; } + /** + * Joins the elements of a collection into a single string, separated by the given separator. + * + * @param collection the collection of strings to join + * @param separator the separator to use between elements + * @return the joined string + */ public static String joinStrings(Collection collection, String separator) { return String.join(separator, collection); } /** - * Joins the elements of a collection with a given separator. This method requires a mapping function to convert the - * elements into strings + * Joins the elements of a collection into a single string, separated by the given separator. This method requires a + * mapping function to convert the elements into strings. * - * @param collection - * @param mapper - * @param separator - * @return + * @param collection the collection of elements to join + * @param mapper the function to map elements to strings + * @param separator the separator to use between elements + * @return the joined string */ public static String join(Collection collection, Function mapper, String separator) { @@ -114,14 +138,12 @@ public static String join(Collection collection, Function mapp } /** + * Joins the elements of a collection into a single string, separated by the given separator. This method uses the + * toString method for each element. * - * Joins the elements of a collection with a given separator. This method uses the toString method for each element. - * - * @param collection - * @param mapper - * @param separator - * @return - * @return + * @param collection the collection of elements to join + * @param separator the separator to use between elements + * @return the joined string */ public static String join(Collection collection, String separator) { @@ -130,11 +152,11 @@ public static String join(Collection collection, String separator) { } /** - * Compares the package of two classes + * Compares the package of two classes. * - * @param firstClassName - * @param secondClassName - * @return true if in the same package, false otherwise + * @param firstClassName the name of the first class + * @param secondClassName the name of the second class + * @return true if both classes are in the same package, false otherwise */ public static boolean inSamePackage(String firstClassName, String secondClassName) { final String firstPackage = getPackage(firstClassName); @@ -143,11 +165,10 @@ public static boolean inSamePackage(String firstClassName, String secondClassNam } /** - * Get the package from a given class name + * Gets the package from a given class name. * - * @param className - * the name of the class - * @return the package if is present in the class name (name contains '.'), empty string otherwise + * @param className the name of the class + * @return the package if present in the class name (name contains '.'), empty string otherwise */ public static String getPackage(String className) { final int lastDot = className.lastIndexOf('.'); @@ -158,7 +179,7 @@ public static String getPackage(String className) { } /** - * Repeat a given string 'repeat' times. if the string to repeat. + * Repeats a given string a specified number of times. *

    * Conditions:
    * - toRepeat == null || repeat < 0 -> null
    @@ -166,9 +187,9 @@ public static String getPackage(String className) { * - toRepeat.isEmpty || repeat == 1 -> toRepeat
    * - else -> toRepeat * repeat * - * @param toRepeat - * @param repeat - * @return + * @param toRepeat the string to repeat + * @param repeat the number of times to repeat the string + * @return the repeated string */ public static String repeat(String toRepeat, int repeat) { if (toRepeat == null || repeat < 0) { @@ -183,6 +204,16 @@ public static String repeat(String toRepeat, int repeat) { return new String(new char[repeat]).replace("\0", toRepeat); } + /** + * Converts an XML Document to a StringBuffer with the specified indentation amount. + * + * @param doc the XML Document to convert + * @param identAmount the amount of indentation + * @return the StringBuffer representation of the XML Document + * @throws TransformerFactoryConfigurationError if there is a configuration error in the TransformerFactory + * @throws TransformerConfigurationException if there is a configuration error in the Transformer + * @throws TransformerException if there is an error during the transformation + */ public static StringBuffer xmlToStringBuffer(Document doc, int identAmount) throws TransformerFactoryConfigurationError, TransformerConfigurationException, TransformerException { final TransformerFactory transfac = TransformerFactory.newInstance(); diff --git a/tdrcLibrary/src/tdrc/vector/IntegerVector2D.java b/tdrcLibrary/src/tdrc/vector/IntegerVector2D.java index e351186e..466ae798 100644 --- a/tdrcLibrary/src/tdrc/vector/IntegerVector2D.java +++ b/tdrcLibrary/src/tdrc/vector/IntegerVector2D.java @@ -8,24 +8,42 @@ * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. under the License. + * specific language governing permissions and limitations under the License. */ package tdrc.vector; +/** + * Represents a 2D vector of integers. + */ public class IntegerVector2D implements Comparable { private int x; private int y; + /** + * Default constructor that initializes the vector to (0, 0). + */ public IntegerVector2D() { } + /** + * Constructor that initializes the vector to the given x and y values. + * + * @param x the x-coordinate of the vector + * @param y the y-coordinate of the vector + */ public IntegerVector2D(int x, int y) { this.setX(x); this.setY(y); } + /** + * Compares this vector to another vector based on magnitude and angle. + * + * @param o the other vector to compare to + * @return 1 if this vector is greater, -1 if less, 0 if equal + */ @Override public int compareTo(IntegerVector2D o) { double magv = Math.sqrt(x * x + y * y); @@ -41,25 +59,50 @@ public int compareTo(IntegerVector2D o) { return anglev == angleo ? 0 : anglev > angleo ? 1 : -1; } + /** + * Calculates the distance between this vector and another vector. + * + * @param o the other vector + * @return the distance between the two vectors + */ public double getDistance(IntegerVector2D o) { double dx = x - o.x; double dy = y - o.y; return Math.sqrt(dx * dx + dy * dy); - } + /** + * Gets the x-coordinate of the vector. + * + * @return the x-coordinate + */ public int getX() { return x; } + /** + * Sets the x-coordinate of the vector. + * + * @param x the new x-coordinate + */ public void setX(int x) { this.x = x; } + /** + * Gets the y-coordinate of the vector. + * + * @return the y-coordinate + */ public int getY() { return y; } + /** + * Sets the y-coordinate of the vector. + * + * @param y the new y-coordinate + */ public void setY(int y) { this.y = y; } diff --git a/tdrcLibrary/src/tdrc/vector/IntegerVector3D.java b/tdrcLibrary/src/tdrc/vector/IntegerVector3D.java index 39885923..cc923991 100644 --- a/tdrcLibrary/src/tdrc/vector/IntegerVector3D.java +++ b/tdrcLibrary/src/tdrc/vector/IntegerVector3D.java @@ -13,21 +13,40 @@ package tdrc.vector; +/** + * Represents a 3D vector of integers. + */ public class IntegerVector3D implements Comparable { private int x; private int y; private int z; + /** + * Default constructor that initializes the vector to (0, 0, 0). + */ public IntegerVector3D() { } + /** + * Constructor that initializes the vector to the given x, y, and z values. + * + * @param x the x-coordinate of the vector + * @param y the y-coordinate of the vector + * @param z the z-coordinate of the vector + */ public IntegerVector3D(int x, int y, int z) { this.setX(x); this.setY(y); this.setZ(z); } + /** + * Compares this vector to another vector based on magnitude and angle. + * + * @param o the other vector to compare to + * @return 1 if this vector is greater, -1 if less, and 0 if equal + */ @Override public int compareTo(IntegerVector3D o) { double magv = Math.sqrt(x * x + y * y + z * z); @@ -43,33 +62,68 @@ public int compareTo(IntegerVector3D o) { return anglev == angleo ? 0 : anglev > angleo ? 1 : -1; } + /** + * Calculates the distance between this vector and another vector in 2D space. + * + * @param o the other vector + * @return the distance between the two vectors + */ public double getDistance(IntegerVector3D o) { double dx = x - o.x; double dy = y - o.y; return Math.sqrt(dx * dx + dy * dy); - } + /** + * Gets the x-coordinate of the vector. + * + * @return the x-coordinate + */ public int getX() { return x; } + /** + * Sets the x-coordinate of the vector. + * + * @param x the x-coordinate to set + */ public void setX(int x) { this.x = x; } + /** + * Gets the y-coordinate of the vector. + * + * @return the y-coordinate + */ public int getY() { return y; } + /** + * Sets the y-coordinate of the vector. + * + * @param y the y-coordinate to set + */ public void setY(int y) { this.y = y; } + /** + * Gets the z-coordinate of the vector. + * + * @return the z-coordinate + */ public int getZ() { return z; } + /** + * Sets the z-coordinate of the vector. + * + * @param z the z-coordinate to set + */ public void setZ(int z) { this.z = z; }