Jak posortować stos mając do dyspozycji tylko operacje Push, Pop i IsEmpty?
Jak posortować stos mając do dyspozycji tylko operacje Push, Pop i IsEmpty?. Najłatwiej zrobić to rekurencyjnie
using System;
using System.Collections.Generic;
namespace CodeKata
{
public static class StackSort
{
/// <summary>
/// Sort the stack in the ascending order
/// </summary>
public static void SortStack<T>(Stack<T> stack) where T : IComparable<T>
{
if (!stack.IsEmpty())
{
T top = stack.Pop();
SortStack(stack);
Insert(stack, top);
}
}
private static void Insert<T>(Stack<T> stack, T value) where T : IComparable<T>
{
if (!stack.IsEmpty())
{
T top = stack.Pop();
// top < value
if (top.CompareTo(value) < 0)
{
Insert(stack, value);
stack.Push(top);
}
else
{
stack.Push(top);
stack.Push(value);
}
}
else
{
stack.Push(value);
}
}
private static bool IsEmpty<T>(this Stack<T> stack) {
return stack.Count == 0;
}
}
}
Powyższy algorytm kreatywnie wykorzystuje stos wysołań jako dodatkową strukturę danych. Najpierw wszystkie elementy ze stosu do posortowania
są zdejmowane a potem po kolei wstawiane na właściwe miejsce. Cała robotę wykonuje metody Insert do której przekazujemy posortowany stos i wartość do wstawienia a ona umieszcza ją na właściwej pozycji (w razie konieczności wywołując się rekurencyjnie).
Przykładowo, w przypadku sortowania stosu [4, 2, 3, 1] drzewo drzewo wywołań rekurencyjnych wygląda tak
SortStack([4, 2, 3, 1])
SortStack([2, 3, 1])
SortStack([3, 1])
SortStack([1])
SortStack([])
[]
Insert([], 1)
[1]
[1]
Insert([1], 3)
Insert([], 3)
[3]
[1, 3]
[1, 3]
Insert([1, 3], 2)
Insert([3], 2)
[2, 3]
[1, 2, 3]
[1, 2, 3]
Insert([1, 2, 3], 4)
Insert([2, 3], 4)
Insert([3], 4)
Insert([], 4)
[4]
[3, 4]
[2, 3, 4]
[1, 2, 3, 4]
[1, 2, 3, 4]
Algorytm działa w czasie O(n2) i wymaga O(n) dodatkowej pamięci (mam nadzieję że uwierzycie mi na słowo – bo udowodnić tego nie potrafię. Może kilka lat temu…). Jest to bardziej widoczne w wersji bez rekurencji – ale o tym innym razem.
Warto też zauważyć, że stosując podobne podejście można rozwiązać inny problem – odwrócenie stosu
/// <summary>
/// Reverse the stack
/// </summary>
public static void ReverseStack<T>(Stack<T> stack)
{
if (!stack.IsEmpty())
{
T top = stack.Pop();
ReverseStack(stack);
InsertAtBottom(stack, top);
}
}
private static void InsertAtBottom<T>(Stack<T> stack, T value)
{
if (!stack.IsEmpty())
{
T top = stack.Pop();
InsertAtBottom(stack, value);
stack.Push(top);
}
else
{
stack.Push(value);
}
}
private static bool IsEmpty<T>(this Stack<T> stack) {
return stack.Count == 0;
}
[Java] Analiza i wizualizacja zależności pomiędzy plikami JAR, część II
W poprzedniej części dowidzieliśmy się, jak utworzyć graf zależności oraz poznaliśmy podstawowe metody wizualizacji. W tym artykule postaram się przedstawić jak różne pojęcia z teorii grafów przekładają się na nasz model (dla przypomnienia – zależności modelujemy jako graf skierowany w którym wierzchołki są plikami JAR i krawędź A→B oznacza, że A zależy od B)
Stopień wierzchołka
Stopień wierzchołka w grafie to liczba krawędzi sąsiadujących z wierzchołkiem. W przypadku grafów skierowanych mówi się o stopniach wyjściowymi i wejściowym – degOut(v), degIn(v) . W grafie zależności jest to odpowiednio liczba projektów od których zależy bezpośrednio dany projekt i liczba projektów które zależą od niego. W mojej opinii najlepszą wizualiacją stopnia jest jego rozmiar, przy czym stopień wejściowy i wyjściowy możemy wizualizować osobno lub łącznie. W przypadku biblioteki Jung najłatwiej osiągnąć to stosując odpowiedni transformer
class VertexShapeTransfomer extends AbstractVertexShapeTransformer
implements Transformer {
public Shape transform(Object v) {
return factory.getEllipse(v)
}
}
def vst = new VertexShapeTransfomer();
vst.setSizeTransformer(new Transformer() {
public Integer transform(Object v) {
return ((g.inDegree(v) + g.outDegree(v)) + 1) * 5
}
})
vs.getRenderContext().setVertexShapeTransformer(vst)
Efekt zastosowanie dla wcześniejszego przykładu wygląda tak

Zbiór zależności modułu s
To nic innego jak zbiór wierzchołków osiągalnych ze źródła s. Do jego wyznaczenia można użyć funkcji DFS-VISIT z algorytmu DFS.
DFS-VISIT(G, u)
u.color ← GRAY
time ← time + 1
u.d ← time
for each v ∈ G.Adj[u]
if v.color == WHITE
v.π ← u
DFS-VISIT(G,v)
u.color ← BLACK
time ← time + 1
u.f ← time
Zbiór zależności s to te wierzchołki które po wywołaniu DFS-VISIT(G, s) mają kolor inny niż biały (tj. zostały co najmniej odwiedzone). W warstwie prezentacji można to zmieniając kolor wierzchołków, np. na zielony
def vertexColor = new Transformer<Integer,Paint>() {
public Paint transform(Object v) {
if(deps.contains(v)) {
return Color.GREEN
} else {
return Color.RED
}
}
}
vs.getRenderContext().setVertexFillPaintTransformer(vertexColor)
W idealnej sytuacji pomiędzy plikami JAR nie powinno być zależności cyklicznych, praktyka pokazuje niestety że czasami jest inaczej. Znalezienie wszystkich cykli w grafie jest samo w sobie ciekawym zagadnienie (patrz Dodatek A), tutaj zastosujemy uproszczony (ale poprawny) algorytm oparty o przeszukiwanie w głąb grafu skierowanego i krawędzie wsteczne
Znając cykle w grafie zależności możemy je zaprezentować na kilka sposobów, co zaprezentują na przykładzie testowego grafu

- Wyświetlić podgraf grafu zależności składający się tylko w wierzchołków wchodzących w skład jakiegoś cyklu

- Dla danego wierzchołka u wyświetlić wszystkie cykle w skład których wchodzi.Przykładowo, dla wierzchołka E jest to tylko cykl A→C→E→A

- Wyświetlić podgraf składający się ze wszystkich cykli określonej długości
Dodatek A Znajdowanie cykli w grafie skierowanym.
Interaktywna konsola Groovy w Eclipse
Eclipse Groovy Console to ciekawy projekt nad którym pracowałem trochę ostatnio w wolnym czasie. Jest to w pełni interaktywna konsola umożliwiająca wykonanie dowolnego kodu w ramach działającej instancji Eclipse
Oto kilka przykładowych akcji które możemy wykonać
-
Wylistowanie wszystkich projektów w bieżącej przestrzeni roboczej
import org.eclipse.core.resources.* IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects()
-
Utworzenie nowego projektu
import org.eclipse.core.runtime.* import org.eclipse.core.resources.* IProgressMonitor progressMonitor = new NullProgressMonitor() IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot() projectName = "NewProjectName" IProject project = root.getProject(projectName) project.create(progressMonitor) project.open(progressMonitor)
-
Otworzenie perspektywy Debug
import org.eclipse.ui.* import org.eclipse.ui.wizards.* import org.eclipse.jface.wizard.* import org.eclipse.swt.widgets.* Display.getDefault().asyncExec { IWorkbenchWindow w = PlatformUI.getWorkbench().getActiveWorkbenchWindow() IPerspectiveRegistry reg = PlatformUI.getWorkbench().getPerspectiveRegistry() String perspId = "org.eclipse.debug.ui.DebugPerspective" w.getActivePage().setPerspective(reg.findPerspectiveWithId(perspId)) }
Sam projekt Eclipse Groovy Console rozwijany jest w serwisie GitHub przy czym ostatnia stabilna wersja wtyczki dostępna jest tutaj.
[Java] Analiza i wizualizacja zależności pomiędzy plikami JAR
Jednym z wyzwań zarządzania duża bazą kodu, szczególnie w sytuacji gdy jest to tzw. Dojrzały/Odziedziczony Kod (ang. Legacy Code) jest zarządzanie zależnościami. Czy jesteście w stanie odpowiedzieć, dla dowolnego modułu, od jakich modułów on zależy i jakie zależą od niego? Czy jesteście w stanie określić pełny zbiór zależności, także tych przechodnich?
Moim ulubionym służącym do tego narzędziem (obok JBoss Tattletale – ale nie wiem jak zmusić go do generowania raportów w XML) jest JarAnalyzer. Rozwiązanie, które zaprezentuję na przykładzie dystrybucji Hibernate 4.1.9.Final jest nieco partyzanckie, ale działa.
Pierwszym krokiem jest wygenerowanie raportu w łatwym do maszynowego przetwarzania formacie XML. Program zakłada, że wszystkie pliki JAR są bezpośrednio we wskazanym katalogu, musimy więc skopiować wszystkie pliki JAR to wspólnego katalogu
find /tmp/hibernate-release-4.1.9.Final/lib -name "*.jar" -exec cp {} /tmp/hibernate-dist-all-jars \;
Okazuje się, że biblioteka JBoss Logging występuje w dwóch wersjach – 3.1.0.GA i 3.1.1.GA – więc usuwamy starszą z nich by uniknąć nieporozumień.
Następnie generujemy raport w formacie XML korzystając z klasy com.kirkk.analyzer.textui.XMLUISummary. W przypadku Windows możemy skorzystać z dostarczonego wraz programem skryptu runxmlsummary.bat, w przypadku innych systemów musimy radzić sobie sami, np.tak
export JARANALYZER_HOME=/path/to/JarAnalyzer-1.2 $JAVA_HOME/bin/java -cp $JARANALYZER_HOME/lib/*:$JARANALYZER_HOME/jaranalyzer-1.2.jar com.kirkk.analyzer.textui.XMLUISummary
Program poprosi nas o wskazanie katalogu z plikami JAR i nazwy pliku wynikowego z raportem. Rezultat znajduje się tutaj i składa się z tagów Jar dla każdego z analizowanym plików. Przykładowo wpis dla hibernate-core-4.1.9.Final.jar wygląda następująco
<Jar name="hibernate-core-4.1.9.Final.jar"> <Summary> <Statistics> <ClassCount>2903</ClassCount> <AbstractClassCount>751</AbstractClassCount> <PackageCount>166</PackageCount> </Statistics> <Metrics> <Abstractness>0.26</Abstractness> <Efferent>5</Efferent> <Afferent>7</Afferent> <Instability>0.42</Instability> <Distance>0.32</Distance> </Metrics> <Packages> <!-- Usunięte z braku miejsca --> </Packages> <OutgoingDependencies> <Jar>jboss-logging-3.1.1.GA.jar</Jar> <Jar>dom4j-1.6.1.jar</Jar> <Jar>hibernate-commons-annotations-4.0.1.Final.jar</Jar> <Jar>antlr-2.7.7.jar</Jar> <Jar>javassist-3.17.1-GA.jar</Jar> </OutgoingDependencies> <IncomingDependencies> <Jar>ehcache-core-2.4.3.jar</Jar> <Jar>hibernate-c3p0-4.1.9.Final.jar</Jar> <Jar>hibernate-ehcache-4.1.9.Final.jar</Jar> <Jar>hibernate-envers-4.1.9.Final.jar</Jar> <Jar>hibernate-infinispan-4.1.9.Final-tests.jar</Jar> <Jar>hibernate-infinispan-4.1.9.Final.jar</Jar> <Jar>hibernate-proxool-4.1.9.Final.jar</Jar> </IncomingDependencies> <Cycles> </Cycles> <UnresolvedDependencies> <Package>org.apache.tools.ant</Package> <Package>org.apache.tools.ant.types</Package> <Package>org.apache.tools.ant.taskdefs</Package> <Package>org.jboss.jandex</Package> <Package>com.fasterxml.classmate</Package> <Package>com.fasterxml.classmate.members</Package> </UnresolvedDependencies> </Summary> </Jar>
i oprócz informacji o zależnościach zawiera cała masę innych przydatnych informacji. Plik ten można bardzo łatwo przetwarzać i wygenerować np. taki raport
Jar File ClassCount AbstractClassCount PackageCount ---------------------------------------------- ---------- ------------------ -------- hibernate-core-4.1.9.Final.jar 2903 751 166 infinispan-core-5.2.0.Beta3.jar 1420 392 90 jgroups-3.2.0.CR1.jar 895 99 24 ehcache-core-2.4.3.jar 732 155 57 javassist-3.17.1-GA.jar 388 41 17 c3p0-0.9.1.jar 382 82 44 proxool-0.8.3.jar 300 78 17 hibernate-envers-4.1.9.Final.jar 249 60 35 antlr-2.7.7.jar 224 52 12 woodstox-core-asl-4.1.1.jar 203 41 16 jboss-marshalling-1.3.15.GA.jar 200 43 4 dom4j-1.6.1.jar 190 39 14 hibernate-infinispan-4.1.9.Final-tests.jar 176 14 14 hibernate-jpa-2.0-api-1.0.1.Final.jar 176 134 4 stax2-api-3.1.1.jar 124 51 11 hibernate-commons-annotations-4.0.1.Final.jar 64 17 7 hibernate-ehcache-4.1.9.Final.jar 58 12 6 jboss-logging-3.1.1.GA.jar 43 16 1 hibernate-infinispan-4.1.9.Final.jar 37 4 10 jboss-marshalling-river-1.3.15.GA.jar 35 2 1 staxmapper-1.1.0.Final.jar 31 8 1 slf4j-api-1.6.1.jar 23 10 3 jboss-transaction-api_1.1_spec-1.0.0.Final.jar 18 8 2 rhq-pluginAnnotations-3.0.4.jar 7 3 1 hibernate-c3p0-4.1.9.Final.jar 3 1 1 hibernate-proxool-4.1.9.Final.jar 3 1 1
Następnym krokiem jest analiza i wizualizacja zależności. Najprościej jest je modelować jako graf skierowany w którym wierzchołki są plikami JAR i krawędź A→B oznacza, że A zależy od B. Idealny do wizualizacji tego typu zależności jest Zest, ale nie jest to lekkie rozwiązania więc wystarczyć musi Jung i skrypt Groovy
Efekt końcowy wygląda mnie więcej tak

Biblioteka JBoss Logging zaciemnia trochę obraz zależności, bo zależy od niej prawie wszystko a ona sama zależy tylko od slf4j. Po jej usunięciu obraz zależności jest bardziej czytelny.
Jeśli uzyskana wizualizacja nas nie satysfakcjonuje, warto spróbować innych algorytmów rozkładu grafu oferowanych przez Jung np. DAGLayout, FRLayout, KKLayout, SpringLayout.
Update
We wcześniejszej części wyraziłem przypuszczenie, że Zest jest idealny do wizualizacji tego typu zależności, które jak się okazało nie do końca odpowiada rzeczywistości
Kod aplikacji generujący powyższy obrazek wygląda następująco
import org.eclipse.swt.*
import org.eclipse.swt.graphics.*
import org.eclipse.swt.layout.*
import org.eclipse.swt.widgets.*
import org.eclipse.jface.viewers.*
import org.eclipse.zest.core.viewers.*
import org.eclipse.zest.core.widgets.*
import org.eclipse.zest.layouts.*;
import org.eclipse.zest.layouts.algorithms.*
class GraphContentProvider implements IGraphEntityContentProvider {
def DirectedGraph g;
GraphContentProvider(DirectedGraph g) {
this.g = g;
}
@Override
public void dispose() {
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
@Override
public Object[] getConnectedTo(Object entity) {
return g.getSuccessors(entity)
}
@Override
public Object[] getElements(Object inputElement) {
return g.getVertices()
}
}
class ZestLabelProvider extends LabelProvider {
@Override
public String getText(Object element) {
if (element instanceof String) {
return element.toString();
}
return null;
}
}
Display d = new Display()
Shell shell = new Shell(d)
shell.with {
setText("Jar Dependency Visualizer")
setLayout(new FillLayout(SWT.VERTICAL))
setSize(1600, 960)
}
viewer = new GraphViewer(shell, SWT.NONE);
viewer.with {
setContentProvider(new GraphContentProvider(g))
setConnectionStyle(ZestStyles.CONNECTIONS_DIRECTED)
setLabelProvider(new ZestLabelProvider())
setLayoutAlgorithm(
new CompositeLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING,
[
new DirectedGraphLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING),
new HorizontalShift(LayoutStyles.NO_LAYOUT_NODE_RESIZING)
] as LayoutAlgorithm[]))
}
viewer.setInput(null);
viewer.reveal("hibernate-core-4.1.9.Final.jar")
shell.open();
while (!shell.isDisposed()) {
while (!d.readAndDispatch()) {
d.sleep();
}
}
Wizualizacja zależności w OSGi
OSGi zmieniło sposób jaki programiści i architekci Java™ tworzą aplikacje – ma jednak kilka wad wynikających najczęściej z braku odpowiednich narzędzi. OSGi wymusza jawne zdefiniowanie zależności, jednak kontrola wzajemnych powiązań pomiędzy pakunkami bez odpowiednich narzędzi jest bardzo trudna. Użytkownikom Eclipse trochę pomaga tutaj PDE, ale trudno uznać je za kompletne rozwiązanie. Jednym z projektów z którym, pomimo jego ograniczeń, wiązałem swego czasu spore nadzieje jest PDF Dependency Visualization który prezentuje zależności w aktywnej Target Platform jako graf skierowany.

Projekt jest niestety nie jest obecnie rozwijany i ma sporo ograniczeń (np. nie uwzględnia poprawnie pakunków częściowych), ale nie istnieje dla niego, według mojej wiedzy, żadna alternatywa. Kod źródłowy jest zresztą prosty, i jest to jeden z projektów który poważnie kusi mnie by się w niego zaangażować.
Instalacja
Plugin można zainstalować korzystając z update-site’a http://download.eclipse.org/eclipse/pde/visualization/updates lub uruchomić ze źródeł dostępnych w repozytorium Git eclipse.pde.incubator.git.
Rozwój projektu
Dla zainteresowanych wsparciem projektu, lista otwartych zgłoszeń jest dostępna tutaj.
Konstrukcja drzewa na podstawie listy scieżek
To zasadniczo proste zadanie. Mają listę ścieżek, np. w postaci nazw węzłów oddzielonych separatorem (A;B;C, A;X) należy skonstruować drzewo. Widziałem jednak to proste zadanie realizowane na tak wiele zakręconych, brzydkich, niewydajnych sposobów, że postanowiłem coś z tym zrobić.
Algorytm
Jest bardzo prosty i składa się z dwóch etapów. Pierwszych jest krokiem jest przetworzenie ścieżek na sekwencję węzłów na podstawie których tworzymy drzewo przeglądają każdą ścieżkę od korzenia dodając brakujące w grafie węzły i krawędzie. Jedynym wyzwaniem jest zapewnienie separacji poszczególnych części zadania tak by można było wielokrotnie wykorzystać kod, co udało mi się całkiem nieźle wykorzystując typy uogólnione
Kod
Najważniejsze interfejsy wyglądają następująco
/**
* Enkapsuluje operacje na węzłach drzewa które budujemy
*/
public interface TreeNodeAccessor<NodeType, NameType> {
NodeType getOrCreate(NodeType parentNode, NameType childNodeName);
}
/**
* Zbiór ścieżek na podstawie którego zbudowane zostanie drzewo
*/
public interface PathSet<NameType> extends Iterable<PathIterable<NameType>> {
}
/**
* Itarator po kolejnych węzłach ścieżki
*/
public interface PathIterable<NameType> extends Iterable<NameType> {
}
Implementacja algorytmu budowy drzewa jest już bardzo prosta
public class TreeBuilder<NodeType, NameType> {
public <NodeAccessor extends TreeNodeAccessor<NodeType, ? super NameType>> NodeType
buildTree(
PathSet<NameType> paths,
NodeType root,
NodeAccessor nodeAccessor) {
for (PathIterable<NameType> path : paths) {
NodeType current = root;
for (NameType nodeName : path) {
current = nodeAccessor.getOrCreate(current, nodeName);
}
}
return root;
}
public static <NodeType, NameType> TreeBuilder<NodeType, NameType> getNew() {
return new TreeBuilder<NodeType, NameType>();
}
}
Przykład
Dla przykładu zbudujmy drzewo swingowe na podstawie ścieżek dostarczonych jako lista ciągów znaków oddzielonych separatorem. W tym celu potrzebny jest akcesor do węzłów drzewa
package utils.impl;
import javax.swing.tree.DefaultMutableTreeNode;
import utils.TreeNodeAccessor;
public class DefaultMutableTreeNodeAccessor implements
TreeNodeAccessor<DefaultMutableTreeNode, Object> {
@Override
public DefaultMutableTreeNode getOrCreate(
DefaultMutableTreeNode parentNode, Object childNodeName) {
int childCount = parentNode.getChildCount();
for (int childIndex = 0; childIndex < childCount; ++childIndex) {
DefaultMutableTreeNode childNode =
(DefaultMutableTreeNode) parentNode.getChildAt(childIndex);
Object userObject = childNode.getUserObject();
if (eq(userObject, childNodeName)) {
return childNode;
}
}
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(childNodeName);
parentNode.add(childNode);
return childNode;
}
private static boolean eq(Object userObject, Object nodeName) {
return userObject == null ? nodeName == null : userObject.equals(nodeName);
}
public static DefaultMutableTreeNodeAccessor getNew() {
return new DefaultMutableTreeNodeAccessor();
}
}
Sam kod budowania drzewa jest bardzo prosty
TreeBuilder<DefaultMutableTreeNode, String>
defaultMutableTreeNodeBuilder = TreeBuilder.getNew();
String[] paths = { "A;B;C", "X;A", "A;X", "Q", "X;Y;Z;R;Q;Q" };
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
TreeNodeAccessor<DefaultMutableTreeNode, Object>
nodeAccessor = DefaultMutableTreeNodeAccessor.getNew();
rootNode = defaultMutableTreeNodeBuilder.buildTree(
PathSetFactory.fromStringPaths(paths, ";"), rootNode,
nodeAccessor);
DefaultTreeModel treeModel = new DefaultTreeModel(rootNode);
JTree tree = new JTree(treeModel);
W efekcie otrzymamy następujące drzewo

Kompletny projekt dostępny jest jako repozytorium jarek-przygodzki/tree-from-paths na GitHub’ie i zawiera między innymi demo prezentujące w postaci drzewiastej zbiór adresów URL

Rozszerzanie listy automatycznych importów w Groovy
Jedną z różnic pomiędzy językami Javy i Groovy jest zbiór automatycznie dostępnych pakietów i klas. W przypadku języka Javą to tylko elementy z pakietu
- java.lang.*
oraz pakiet bieżący. W Groovy automatycznie importowane są następujące klasy i pakiety
- java.io.*
- java.lang.*
- java.math.BigDecimal
- java.math.BigInteger
- java.net.*
- java.util.*
- groovy.lang.*
- groovy.util.*
Co więcej, od wersji 1.8 zbiór ten można łatwo rozszerzyć korzystając z Compilation customizers.
import org.codehaus.groovy.control.customizers.ImportCustomizer
import org.codehaus.groovy.control.CompilerConfiguration
def importCustomizer = new ImportCustomizer()
// package import
importCustomizer.addStarImports('java.util.concurrent')
// static star imports
importCustomizer.addStaticStar('java.lang.Math')
def configuration = new CompilerConfiguration()
configuration.addCompilationCustomizers(importCustomizer)
def shell = new GroovyShell(configuration)
shell.evaluate """
TimeUnit.SECONDS.toMillis(1);
"""
A co z groovy.ui.Console? Jeśli przyjrzymy się źródłom tej klasy, to okaże się że nie istnieje możliwość przekazania własnych ustawień kompilatora. W teorii wystarczyłoby napisać rozszerzenie klasy groovy.ui.Console
import groovy.ui.Console
import groovy.lang.Binding;
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
import org.codehaus.groovy.control.customizers.ImportCustomizer
import org.codehaus.groovy.runtime.*
import javax.swing.*
class ConsoleExt extends Console {
ConsoleExt() {
this(new Binding())
}
ConsoleExt(Binding binding) {
this(null, binding)
}
ConsoleExt(ClassLoader parent) {
this(parent, new Binding())
}
ConsoleExt(ClassLoader parent, Binding binding) {
super(parent, binding)
}
void newScript(ClassLoader parent, Binding binding) {
def importCustomizer = new ImportCustomizer()
// package import
importCustomizer.addStarImports('java.util.concurrent')
// static star imports
importCustomizer.addStaticStar('java.lang.Math')
config = new CompilerConfiguration()
config.addCompilationCustomizers(importCustomizer)
if (threadInterrupt) config.addCompilationCustomizers(
new ASTTransformationCustomizer(ThreadInterrupt))
shell = new GroovyShell(parent, binding, config)
}
}
Niestety, w chwili obecnej takie rozwiązanie nie zadziała ze względu na błąd GROOVY-4670.
Uwaga.
Błąd GROOVY-4670 został naprawiony w wersji 2.1.0-rc-1 więc powyższy kod działa już poprawnie.


