001/* 002 * Copyright (c) 2012-2021 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010 011package org.eclipse.golo.cli.command.spi; 012 013import org.eclipse.golo.compiler.GoloCompilationException; 014import gololang.Messages; 015import gololang.ir.GoloModule; 016 017import java.util.Comparator; 018import java.util.Set; 019import java.util.function.Consumer; 020import java.util.function.Function; 021import java.lang.invoke.MethodHandle; 022import java.io.File; 023 024import static java.lang.invoke.MethodHandles.publicLookup; 025import static java.lang.invoke.MethodType.methodType; 026 027import static gololang.Messages.*; 028 029 030public interface CliCommand { 031 032 Comparator<GoloModule> MODULE_COMPARATOR = (GoloModule m1, GoloModule m2) -> { 033 if (m1 == null && m2 != null) { return -1; } 034 if (m1 != null && m2 == null) { return 1; } 035 if (m1 == null && m2 == null) { return 0; } 036 if (m1.hasMacros() && !m2.hasMacros()) { return -1; } 037 if (!m1.hasMacros() && m2.hasMacros()) { return 1; } 038 Set<String> m1Used = m1.getUsedModules(); 039 Set<String> m2Used = m2.getUsedModules(); 040 if (m1Used.contains(m2.getPackageAndClass().toString())) { return 1; } 041 if (m2Used.contains(m1.getPackageAndClass().toString())) { return -1; } 042 if (m1.getImports().stream().anyMatch((mi) -> mi.getPackageAndClass().equals(m2.getPackageAndClass()))) { return 1; } 043 if (m2.getImports().stream().anyMatch((mi) -> mi.getPackageAndClass().equals(m1.getPackageAndClass()))) { return -1; } 044 if (m1.hasMain() && !m2.hasMain()) { return 1; } 045 if (m2.hasMain() && !m1.hasMain()) { return -1; } 046 return 0; 047 }; 048 049 void execute() throws Throwable; 050 051 default void callRun(Class<?> klass, String[] arguments) throws Throwable { 052 MethodHandle main; 053 try { 054 main = publicLookup().findStatic(klass, "main", methodType(void.class, String[].class)); 055 } catch (NoSuchMethodException e) { 056 throw new NoMainMethodException().initCause(e); 057 } 058 main.invoke(arguments); 059 } 060 061 default boolean canRead(File source) { 062 if (source == null) { return false; } 063 if (!source.canRead()) { 064 warning(message("file_not_found", source.getPath())); 065 return false; 066 } 067 return true; 068 } 069 070 default Consumer<File> wrappedAction(boolean exitOnError, GolofileAction action) { 071 return source -> { 072 if (canRead(source)) { 073 try { 074 action.accept(source); 075 } catch (GoloCompilationException e) { 076 handleCompilationException(e, exitOnError); 077 } catch (Throwable e) { 078 handleThrowable(e, exitOnError); 079 } 080 } 081 }; 082 } 083 084 default Consumer<File> wrappedAction(GolofileAction action) { 085 return wrappedAction(false, action); 086 } 087 088 default <T, R> Function<T, R> wrappedTreatment(GoloCompilationTreatment<T, R> t) { 089 return data -> { 090 if (data == null) { 091 return null; 092 } 093 try { 094 return t.apply(data); 095 } catch (GoloCompilationException e) { 096 handleCompilationException(e, false); 097 return null; 098 } catch (Throwable e) { 099 handleThrowable(e, false); 100 return null; 101 } 102 }; 103 } 104 105 default <T> Function<T, T> displayInfo(String message) { 106 if (this.verbose()) { 107 return object -> { 108 if (object != null) { 109 info(String.format(message, object)); 110 } 111 return object; 112 }; 113 } 114 return Function.identity(); 115 } 116 117 default boolean verbose() { 118 return false; 119 } 120 121 default void handleCompilationException(GoloCompilationException e) { 122 handleCompilationException(e, true); 123 } 124 125 default void handleCompilationException(GoloCompilationException e, boolean exit) { 126 Messages.error(e.getLocalizedMessage()); 127 for (GoloCompilationException.Problem problem : e.getProblems()) { 128 Messages.error(problem.getDescription(), " "); 129 Throwable cause = problem.getCause(); 130 if (cause != null) { 131 handleThrowable(cause, false, gololang.Runtime.debugMode(), " "); 132 } 133 } 134 if (exit) { 135 System.exit(1); 136 } 137 } 138 139 default void handleThrowable(Throwable e) { 140 handleThrowable(e, gololang.Runtime.showStackTrace()); 141 } 142 143 default void handleThrowable(Throwable e, boolean exit) { 144 handleThrowable(e, exit, gololang.Runtime.debugMode() || gololang.Runtime.showStackTrace()); 145 } 146 147 default void handleThrowable(Throwable e, boolean exit, boolean withStack) { 148 handleThrowable(e, exit, withStack, ""); 149 } 150 151 default void handleThrowable(Throwable e, boolean exit, boolean withStack, String indent) { 152 Messages.error(e.getLocalizedMessage(), indent); 153 if (e.getCause() != null) { 154 Messages.error(e.getCause().getLocalizedMessage(), " " + indent); 155 } 156 if (withStack) { 157 Messages.printStackTrace(e); 158 } else { 159 Messages.error(Messages.message("use_debug")); 160 } 161 if (exit) { 162 System.exit(1); 163 } 164 } 165 166 class NoMainMethodException extends NoSuchMethodException { } 167 168 @FunctionalInterface 169 interface GolofileAction { 170 void accept(File source) throws Throwable; 171 } 172 173 @FunctionalInterface 174 interface GoloCompilationTreatment<T, R> { 175 R apply(T o) throws Throwable; 176 } 177 178}