Jarek Przygódzki. Blog programisty

Ogólne przemyślenia na temat rzemiosła / sztuki / nauki programowania i tematów pokrewnych.

Jak posortować stos mając do dyspozycji tylko operacje Push, Pop i IsEmpty?

Skomentuj »

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;
}

Written by Jarek Przygódzki

Maj 8, 2013 at 11:34 pm

[Java] Analiza i wizualizacja zależności pomiędzy plikami JAR, część II

Skomentuj »

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
hibernate-release-4.1.9-dep-vis-vertex-degree

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)

Zależności (w tym przechodnie) modułu g, isVisited) def selectedNode = 'hibernate-core-4.1.9.Final.jar'
Cykle

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
directed-graph-with-cycles

  • Wyświetlić podgraf grafu zależności składający się tylko w wierzchołków wchodzących w skład jakiegoś cyklu
    directed-graph-with-cycles-only
  • 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
    cycles-containing-vertex-e
  • Wyświetlić podgraf składający się ze wszystkich cykli określonej długości

Dodatek A Znajdowanie cykli w grafie skierowanym.

Written by Jarek Przygódzki

Kwiecień 11, 2013 at 10:22 pm

Interaktywna konsola Groovy w Eclipse

Skomentuj »

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

interactive-groovy-console

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.

Written by Jarek Przygódzki

Marzec 14, 2013 at 7:54 pm

Napisane w Eclipse, Groovy

Tagi: , ,

[Java] Analiza i wizualizacja zależności pomiędzy plikami JAR

z jednym komentarzem

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

hibernate-release-4.1.9-deps
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.

hibernate-release-4.1.9-deps-no-jboss-loggingl

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ścijar-deps-vis-with-zest
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();
	}
}

Written by Jarek Przygódzki

Luty 10, 2013 at 4:27 pm

Napisane w Java

Tagi: , , ,

Wizualizacja zależności w OSGi

Skomentuj »

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.
pde-visualization
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.

Written by Jarek Przygódzki

Styczeń 28, 2013 at 9:04 pm

Konstrukcja drzewa na podstawie listy scieżek

Skomentuj »

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
tree-from-paths-demo
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
tree-from-urls-demo

Written by Jarek Przygódzki

Styczeń 27, 2013 at 10:21 pm

Napisane w Java

Tagi: , ,

Rozszerzanie listy automatycznych importów w Groovy

Skomentuj »

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.

Written by Jarek Przygódzki

Grudzień 22, 2012 at 2:47 pm

Follow

Otrzymuj każdy nowy wpis na swoją skrzynkę e-mail.