mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 10:40:06 -08:00
[card.plugin] Symbols support: download and display. Please try it, if you don't like it, will remove or make configurable. Thanks to SillyFreak (Laterna Magica) for GathererSymbols downloader code.
This commit is contained in:
parent
7d9d2bc744
commit
c650c43f94
35 changed files with 1758 additions and 25 deletions
|
|
@ -57,6 +57,11 @@
|
||||||
<artifactId>swingx</artifactId>
|
<artifactId>swingx</artifactId>
|
||||||
<version>1.6.1</version>
|
<version>1.6.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetlang</groupId>
|
||||||
|
<artifactId>jetlang</artifactId>
|
||||||
|
<version>0.2.4</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,21 @@ public class MageFrame extends javax.swing.JFrame {
|
||||||
Separator separator = new javax.swing.JToolBar.Separator();
|
Separator separator = new javax.swing.JToolBar.Separator();
|
||||||
mageToolbar.add(separator);
|
mageToolbar.add(separator);
|
||||||
|
|
||||||
|
JButton btnDownloadSymbols = new JButton("Symbols");
|
||||||
|
btnDownloadSymbols.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
||||||
|
btnDownloadSymbols.setFocusable(false);
|
||||||
|
btnDownloadSymbols.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||||
|
btnDownloadSymbols.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||||
|
btnDownloadSymbols.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
btnSymbolsActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mageToolbar.add(btnDownloadSymbols);
|
||||||
|
|
||||||
|
separator = new javax.swing.JToolBar.Separator();
|
||||||
|
mageToolbar.add(separator);
|
||||||
|
|
||||||
JButton btnDownload = new JButton("Images");
|
JButton btnDownload = new JButton("Images");
|
||||||
btnDownload.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
btnDownload.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
||||||
btnDownload.setFocusable(false);
|
btnDownload.setFocusable(false);
|
||||||
|
|
@ -173,6 +188,12 @@ public class MageFrame extends javax.swing.JFrame {
|
||||||
}
|
}
|
||||||
Plugins.getInstance().downloadImage(allCards);
|
Plugins.getInstance().downloadImage(allCards);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void btnSymbolsActionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
if (JOptionPane.showConfirmDialog(null, "Do you want to download mana symbols?") == JOptionPane.OK_OPTION) {
|
||||||
|
Plugins.getInstance().downloadSymbols();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void showGame(UUID gameId, UUID playerId) {
|
public void showGame(UUID gameId, UUID playerId) {
|
||||||
this.tablesPane.hideTables();
|
this.tablesPane.hideTables();
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ public interface MagePlugins {
|
||||||
boolean isCounterPluginLoaded();
|
boolean isCounterPluginLoaded();
|
||||||
void sortPermanents(Map<String, JComponent> ui, Collection<MagePermanent> permanents);
|
void sortPermanents(Map<String, JComponent> ui, Collection<MagePermanent> permanents);
|
||||||
void downloadImage(Set<Card> allCards);
|
void downloadImage(Set<Card> allCards);
|
||||||
|
void downloadSymbols();
|
||||||
int getGamesPlayed();
|
int getGamesPlayed();
|
||||||
void addGamesPlayed();
|
void addGamesPlayed();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,11 @@ public class Plugins implements MagePlugins {
|
||||||
public void downloadImage(Set<mage.cards.Card> allCards) {
|
public void downloadImage(Set<mage.cards.Card> allCards) {
|
||||||
if (this.cardPlugin != null) this.cardPlugin.downloadImages(allCards);
|
if (this.cardPlugin != null) this.cardPlugin.downloadImages(allCards);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void downloadSymbols() {
|
||||||
|
if (this.cardPlugin != null) this.cardPlugin.downloadSymbols();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getGamesPlayed() {
|
public int getGamesPlayed() {
|
||||||
|
|
|
||||||
|
|
@ -28,4 +28,5 @@ public interface CardPlugin extends Plugin {
|
||||||
MagePermanent getMageCard(CardView permanent, CardDimensions dimension, UUID gameId, ActionCallback callback);
|
MagePermanent getMageCard(CardView permanent, CardDimensions dimension, UUID gameId, ActionCallback callback);
|
||||||
void sortPermanents(Map<String, JComponent> ui, Collection<MagePermanent> cards);
|
void sortPermanents(Map<String, JComponent> ui, Collection<MagePermanent> cards);
|
||||||
void downloadImages(Set<Card> allCards);
|
void downloadImages(Set<Card> allCards);
|
||||||
|
void downloadSymbols();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,12 @@
|
||||||
<version>0.8.4</version>
|
<version>0.8.4</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetlang</groupId>
|
||||||
|
<artifactId>jetlang</artifactId>
|
||||||
|
<version>0.2.4</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
||||||
|
|
@ -314,10 +314,13 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti
|
||||||
super.paintChildren(g);
|
super.paintChildren(g);
|
||||||
|
|
||||||
if (showCastingCost && !isAnimationPanel && cardWidth < 200 && cardWidth > 60) {
|
if (showCastingCost && !isAnimationPanel && cardWidth < 200 && cardWidth > 60) {
|
||||||
//TODO:uncomment
|
String manaCost = ManaSymbols.getStringManaCost(gameCard.getManaCost());
|
||||||
/*int width = ManaSymbols.getWidth(gameCard.getManaCost());
|
int width = ManaSymbols.getWidth(manaCost);
|
||||||
ManaSymbols.draw(g, gameCard.getManaCost(), cardXOffset + 8, cardHeight - 9);
|
if (hasImage) {
|
||||||
*/
|
ManaSymbols.draw(g, manaCost, cardXOffset + cardWidth - width - 5, cardYOffset + 5);
|
||||||
|
} else {
|
||||||
|
ManaSymbols.draw(g, manaCost, cardXOffset + 8, cardHeight - 9);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,19 @@ package org.mage.card.arcane;
|
||||||
|
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.mage.plugins.card.constants.Constants;
|
import org.mage.plugins.card.constants.Constants;
|
||||||
|
import org.mage.plugins.card.images.ImageCache;
|
||||||
|
import org.mage.plugins.card.utils.BufferedImageBuilder;
|
||||||
|
|
||||||
public class ManaSymbols {
|
public class ManaSymbols {
|
||||||
private static final Logger log = Logger.getLogger(ManaSymbols.class);
|
private static final Logger log = Logger.getLogger(ManaSymbols.class);
|
||||||
|
|
@ -18,9 +24,16 @@ public class ManaSymbols {
|
||||||
static public void loadImages () {
|
static public void loadImages () {
|
||||||
String[] symbols = new String[] {"0", "1", "10", "11", "12", "15", "16", "2", "3", "4", "5", "6", "7", "8", "9", "B", "BG",
|
String[] symbols = new String[] {"0", "1", "10", "11", "12", "15", "16", "2", "3", "4", "5", "6", "7", "8", "9", "B", "BG",
|
||||||
"BR", "G", "GU", "GW", "R", "RG", "RW", "S", "T", "U", "UB", "UR", "W", "WB", "WU", "X", "Y", "Z", "slash"};
|
"BR", "G", "GU", "GW", "R", "RG", "RW", "S", "T", "U", "UB", "UR", "W", "WB", "WU", "X", "Y", "Z", "slash"};
|
||||||
//TODO: replace by downloading
|
for (String symbol : symbols) {
|
||||||
for (String symbol : symbols)
|
File file = new File(Constants.RESOURCE_PATH_MANA + "/" + symbol + ".jpg");
|
||||||
manaImages.put(symbol, UI.getImageIcon(Constants.RESOURCE_PATH_MANA + "/" + symbol + ".png").getImage());
|
BufferedImageBuilder builder = new BufferedImageBuilder();
|
||||||
|
Rectangle r = new Rectangle(11, 11);
|
||||||
|
try {
|
||||||
|
Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
|
||||||
|
BufferedImage resized = ImageCache.getResizedImage(builder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r);
|
||||||
|
manaImages.put(symbol, resized);
|
||||||
|
} catch (Exception e) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static public void draw (Graphics g, String manaCost, int x, int y) {
|
static public void draw (Graphics g, String manaCost, int x, int y) {
|
||||||
|
|
@ -32,7 +45,7 @@ public class ManaSymbols {
|
||||||
String symbol = tok.nextToken().substring(0);
|
String symbol = tok.nextToken().substring(0);
|
||||||
Image image = manaImages.get(symbol);
|
Image image = manaImages.get(symbol);
|
||||||
if (image == null) {
|
if (image == null) {
|
||||||
log.error("Symbol not recognized \"" + symbol + "\" in mana cost: " + manaCost);
|
//log.error("Symbol not recognized \"" + symbol + "\" in mana cost: " + manaCost);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
g.drawImage(image, x, y, null);
|
g.drawImage(image, x, y, null);
|
||||||
|
|
@ -40,7 +53,15 @@ public class ManaSymbols {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static public int getWidth (String manaCost) {
|
static public String getStringManaCost(List<String> manaCost) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (String s : manaCost) {
|
||||||
|
sb.append(s);
|
||||||
|
}
|
||||||
|
return sb.toString().replace("{", "").replace("}", " ").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
static public int getWidth(String manaCost) {
|
||||||
int width = 0;
|
int width = 0;
|
||||||
manaCost = manaCost.replace("\\", "");
|
manaCost = manaCost.replace("\\", "");
|
||||||
StringTokenizer tok = new StringTokenizer(manaCost, " ");
|
StringTokenizer tok = new StringTokenizer(manaCost, " ");
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
package org.mage.plugins.card;
|
package org.mage.plugins.card;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Frame;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.WindowAdapter;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -9,6 +13,7 @@ import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JLayeredPane;
|
import javax.swing.JLayeredPane;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
|
|
||||||
|
|
@ -27,7 +32,12 @@ import net.xeoh.plugins.base.annotations.meta.Author;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.mage.card.arcane.CardPanel;
|
import org.mage.card.arcane.CardPanel;
|
||||||
|
import org.mage.card.arcane.ManaSymbols;
|
||||||
import org.mage.plugins.card.constants.Constants;
|
import org.mage.plugins.card.constants.Constants;
|
||||||
|
import org.mage.plugins.card.dl.DownloadGui;
|
||||||
|
import org.mage.plugins.card.dl.DownloadJob;
|
||||||
|
import org.mage.plugins.card.dl.Downloader;
|
||||||
|
import org.mage.plugins.card.dl.sources.GathererSymbols;
|
||||||
import org.mage.plugins.card.images.DownloadPictures;
|
import org.mage.plugins.card.images.DownloadPictures;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -68,6 +78,7 @@ public class CardPluginImpl implements CardPlugin {
|
||||||
|
|
||||||
@PluginLoaded
|
@PluginLoaded
|
||||||
public void newPlugin(CardPlugin plugin) {
|
public void newPlugin(CardPlugin plugin) {
|
||||||
|
ManaSymbols.loadImages();
|
||||||
log.info(plugin.toString() + " has been loaded.");
|
log.info(plugin.toString() + " has been loaded.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,6 +91,7 @@ public class CardPluginImpl implements CardPlugin {
|
||||||
CardPanel cardPanel = new CardPanel(permanent, gameId, true, callback);
|
CardPanel cardPanel = new CardPanel(permanent, gameId, true, callback);
|
||||||
cardPanel.setShowCastingCost(true);
|
cardPanel.setShowCastingCost(true);
|
||||||
cardPanel.setCardBounds(0, 0, dimension.frameWidth, dimension.frameHeight);
|
cardPanel.setCardBounds(0, 0, dimension.frameWidth, dimension.frameHeight);
|
||||||
|
cardPanel.setShowCastingCost(true);
|
||||||
return cardPanel;
|
return cardPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,6 +100,7 @@ public class CardPluginImpl implements CardPlugin {
|
||||||
CardPanel cardPanel = new CardPanel(permanent, gameId, true, callback);
|
CardPanel cardPanel = new CardPanel(permanent, gameId, true, callback);
|
||||||
cardPanel.setShowCastingCost(true);
|
cardPanel.setShowCastingCost(true);
|
||||||
cardPanel.setCardBounds(0, 0, dimension.frameWidth, dimension.frameHeight);
|
cardPanel.setCardBounds(0, 0, dimension.frameWidth, dimension.frameHeight);
|
||||||
|
cardPanel.setShowCastingCost(true);
|
||||||
return cardPanel;
|
return cardPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -390,4 +403,28 @@ public class CardPluginImpl implements CardPlugin {
|
||||||
public void downloadImages(Set<Card> allCards) {
|
public void downloadImages(Set<Card> allCards) {
|
||||||
DownloadPictures.startDownload(null, allCards);
|
DownloadPictures.startDownload(null, allCards);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void downloadSymbols() {
|
||||||
|
final DownloadGui g = new DownloadGui(new Downloader());
|
||||||
|
|
||||||
|
Iterable<DownloadJob> it = new GathererSymbols();
|
||||||
|
|
||||||
|
for(DownloadJob job:it) {
|
||||||
|
g.getDownloader().add(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
JDialog d = new JDialog((Frame) null, "Download pictures", false);
|
||||||
|
d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||||
|
d.addWindowListener(new WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowClosing(WindowEvent e) {
|
||||||
|
g.getDownloader().dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
d.setLayout(new BorderLayout());
|
||||||
|
d.add(g);
|
||||||
|
d.pack();
|
||||||
|
d.setVisible(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,14 @@ import java.awt.Rectangle;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
public class Constants {
|
public class Constants {
|
||||||
public static final String RESOURCE_PATH = "/images";
|
public static final String RESOURCE_PATH_MANA = IO.imageBaseDir + "symbols" + File.separator + "large";
|
||||||
public static final String RESOURCE_PATH_MANA = resourcePath("mana");
|
|
||||||
public static final Rectangle CARD_SIZE_FULL = new Rectangle(101, 149);
|
public static final Rectangle CARD_SIZE_FULL = new Rectangle(101, 149);
|
||||||
|
|
||||||
public interface IO {
|
public interface IO {
|
||||||
public static final String imageBaseDir = "." + File.separator + "plugins" + File.separator + "images" + File.separator;
|
public static final String imageBaseDir = "plugins" + File.separator + "images" + File.separator;
|
||||||
public static final String IMAGE_PROPERTIES_FILE = "image.url.properties";
|
public static final String IMAGE_PROPERTIES_FILE = "image.url.properties";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String CARD_IMAGE_PATH_TEMPLATE = "." + File.separator + "plugins" + File.separator + "images/{set}/{name}.{collector}.full.jpg";
|
public static final String CARD_IMAGE_PATH_TEMPLATE = "." + File.separator + "plugins" + File.separator + "images/{set}/{name}.{collector}.full.jpg";
|
||||||
|
|
||||||
/**
|
|
||||||
* Build resource path.
|
|
||||||
*
|
|
||||||
* @param folder
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static String resourcePath(String folder) {
|
|
||||||
return RESOURCE_PATH + "/" + folder;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,195 @@
|
||||||
|
/**
|
||||||
|
* DownloadGui.java
|
||||||
|
*
|
||||||
|
* Created on 25.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl;
|
||||||
|
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.beans.IndexedPropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.BoundedRangeModel;
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.DefaultBoundedRangeModel;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JProgressBar;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
|
||||||
|
import org.mage.plugins.card.dl.DownloadJob.State;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class DownloadGui.
|
||||||
|
*
|
||||||
|
* @version V0.0 25.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class DownloadGui extends JPanel {
|
||||||
|
private static final long serialVersionUID = -7346572382493844327L;
|
||||||
|
|
||||||
|
private final Downloader d;
|
||||||
|
private final DownloadListener l = new DownloadListener();
|
||||||
|
private final BoundedRangeModel model = new DefaultBoundedRangeModel(0, 0, 0, 0);
|
||||||
|
private final JProgressBar progress = new JProgressBar(model);
|
||||||
|
|
||||||
|
private final Map<DownloadJob, DownloadPanel> progresses = new HashMap<DownloadJob, DownloadPanel>();
|
||||||
|
private final JPanel panel = new JPanel();
|
||||||
|
|
||||||
|
public DownloadGui(Downloader d) {
|
||||||
|
super(new BorderLayout());
|
||||||
|
this.d = d;
|
||||||
|
d.addPropertyChangeListener(l);
|
||||||
|
|
||||||
|
JPanel p = new JPanel(new BorderLayout());
|
||||||
|
p.setBorder(BorderFactory.createTitledBorder("Progress:"));
|
||||||
|
p.add(progress);
|
||||||
|
JButton b = new JButton("X");
|
||||||
|
b.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
getDownloader().dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
p.add(b, BorderLayout.EAST);
|
||||||
|
add(p, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
|
||||||
|
JScrollPane pane = new JScrollPane(panel);
|
||||||
|
pane.setPreferredSize(new Dimension(500, 300));
|
||||||
|
add(pane);
|
||||||
|
for(int i = 0; i < d.getJobs().size(); i++)
|
||||||
|
addJob(i, d.getJobs().get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Downloader getDownloader() {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DownloadListener implements PropertyChangeListener {
|
||||||
|
@Override
|
||||||
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
|
String name = evt.getPropertyName();
|
||||||
|
if(evt.getSource() instanceof DownloadJob) {
|
||||||
|
DownloadPanel p = progresses.get(evt.getSource());
|
||||||
|
if("state".equals(name)) {
|
||||||
|
if(evt.getOldValue() == State.FINISHED || evt.getOldValue() == State.ABORTED) {
|
||||||
|
changeProgress(-1, 0);
|
||||||
|
}
|
||||||
|
if(evt.getNewValue() == State.FINISHED || evt.getOldValue() == State.ABORTED) {
|
||||||
|
changeProgress(+1, 0);
|
||||||
|
}
|
||||||
|
if(p != null) {
|
||||||
|
p.setVisible(p.getJob().getState() != State.FINISHED);
|
||||||
|
p.revalidate();
|
||||||
|
}
|
||||||
|
} else if("message".equals(name)) {
|
||||||
|
if(p != null) {
|
||||||
|
JProgressBar bar = p.getBar();
|
||||||
|
String message = p.getJob().getMessage();
|
||||||
|
bar.setStringPainted(message != null);
|
||||||
|
bar.setString(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(evt.getSource() == d) {
|
||||||
|
if("jobs".equals(name)) {
|
||||||
|
IndexedPropertyChangeEvent ev = (IndexedPropertyChangeEvent) evt;
|
||||||
|
int index = ev.getIndex();
|
||||||
|
|
||||||
|
DownloadJob oldValue = (DownloadJob) ev.getOldValue();
|
||||||
|
if(oldValue != null) removeJob(index, oldValue);
|
||||||
|
|
||||||
|
DownloadJob newValue = (DownloadJob) ev.getNewValue();
|
||||||
|
if(newValue != null) addJob(index, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void addJob(int index, DownloadJob job) {
|
||||||
|
job.addPropertyChangeListener(l);
|
||||||
|
changeProgress(0, +1);
|
||||||
|
DownloadPanel p = new DownloadPanel(job);
|
||||||
|
progresses.put(job, p);
|
||||||
|
panel.add(p, index);
|
||||||
|
panel.revalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void removeJob(int index, DownloadJob job) {
|
||||||
|
assert progresses.get(job) == panel.getComponent(index);
|
||||||
|
job.removePropertyChangeListener(l);
|
||||||
|
changeProgress(0, -1);
|
||||||
|
progresses.remove(job);
|
||||||
|
panel.remove(index);
|
||||||
|
panel.revalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void changeProgress(int progress, int total) {
|
||||||
|
progress += model.getValue();
|
||||||
|
total += model.getMaximum();
|
||||||
|
model.setMaximum(total);
|
||||||
|
model.setValue(progress);
|
||||||
|
this.progress.setStringPainted(true);
|
||||||
|
this.progress.setString(progress + "/" + total);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DownloadPanel extends JPanel {
|
||||||
|
private static final long serialVersionUID = 1187986738303477168L;
|
||||||
|
|
||||||
|
private DownloadJob job;
|
||||||
|
private JProgressBar bar;
|
||||||
|
|
||||||
|
public DownloadPanel(DownloadJob job) {
|
||||||
|
super(new BorderLayout());
|
||||||
|
this.job = job;
|
||||||
|
|
||||||
|
setBorder(BorderFactory.createTitledBorder(job.getName()));
|
||||||
|
add(bar = new JProgressBar(job.getProgress()));
|
||||||
|
JButton b = new JButton("X");
|
||||||
|
b.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
switch(getJob().getState()) {
|
||||||
|
case NEW:
|
||||||
|
case WORKING:
|
||||||
|
getJob().setState(State.ABORTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
add(b, BorderLayout.EAST);
|
||||||
|
|
||||||
|
if(job.getState() == State.FINISHED | job.getState() == State.ABORTED) {
|
||||||
|
changeProgress(+1, 0);
|
||||||
|
}
|
||||||
|
setVisible(job.getState() != State.FINISHED);
|
||||||
|
|
||||||
|
String message = job.getMessage();
|
||||||
|
bar.setStringPainted(message != null);
|
||||||
|
bar.setString(message);
|
||||||
|
|
||||||
|
Dimension d = getPreferredSize();
|
||||||
|
d.width = Integer.MAX_VALUE;
|
||||||
|
setMaximumSize(d);
|
||||||
|
// d.width = 500;
|
||||||
|
// setMinimumSize(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownloadJob getJob() {
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JProgressBar getBar() {
|
||||||
|
return bar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
/**
|
||||||
|
* DownloadJob.java
|
||||||
|
*
|
||||||
|
* Created on 25.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
|
||||||
|
import javax.swing.BoundedRangeModel;
|
||||||
|
import javax.swing.DefaultBoundedRangeModel;
|
||||||
|
|
||||||
|
import org.mage.plugins.card.dl.beans.properties.Property;
|
||||||
|
import org.mage.plugins.card.dl.lm.AbstractLaternaBean;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class DownloadJob.
|
||||||
|
*
|
||||||
|
* @version V0.0 25.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class DownloadJob extends AbstractLaternaBean {
|
||||||
|
public static enum State {
|
||||||
|
NEW, WORKING, FINISHED, ABORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final Source source;
|
||||||
|
private final Destination destination;
|
||||||
|
private final Property<State> state = properties.property("state", State.NEW);
|
||||||
|
private final Property<String> message = properties.property("message");
|
||||||
|
private final Property<Exception> error = properties.property("error");
|
||||||
|
private final BoundedRangeModel progress = new DefaultBoundedRangeModel();
|
||||||
|
|
||||||
|
public DownloadJob(String name, Source source, Destination destination) {
|
||||||
|
this.name = name;
|
||||||
|
this.source = source;
|
||||||
|
this.destination = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the job's state. If the state is {@link State#ABORTED}, it instead sets the error to "ABORTED"
|
||||||
|
*/
|
||||||
|
public void setState(State state) {
|
||||||
|
if(state == State.ABORTED) setError("ABORTED");
|
||||||
|
else this.state.setValue(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the job's state to {@link State#ABORTED} and the error message to the given message. Logs a warning
|
||||||
|
* with the given message.
|
||||||
|
*/
|
||||||
|
public void setError(String message) {
|
||||||
|
setError(message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the job's state to {@link State#ABORTED} and the error to the given exception. Logs a warning with the
|
||||||
|
* given exception.
|
||||||
|
*/
|
||||||
|
public void setError(Exception error) {
|
||||||
|
setError(null, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the job's state to {@link State#ABORTED} and the error to the given exception. Logs a warning with the
|
||||||
|
* given message and exception.
|
||||||
|
*/
|
||||||
|
public void setError(String message, Exception error) {
|
||||||
|
if(message == null) message = error.toString();
|
||||||
|
log.warn(message, error);
|
||||||
|
this.state.setValue(State.ABORTED);
|
||||||
|
this.error.setValue(error);
|
||||||
|
this.message.setValue(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the job's message.
|
||||||
|
*/
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message.setValue(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundedRangeModel getProgress() {
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public State getState() {
|
||||||
|
return state.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Exception getError() {
|
||||||
|
return error.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Source getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Destination getDestination() {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Source fromURL(final String url) {
|
||||||
|
return fromURL(null, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Source fromURL(final URL url) {
|
||||||
|
return fromURL(null, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Source fromURL(final Proxy proxy, final String url) {
|
||||||
|
return new Source() {
|
||||||
|
private URLConnection c;
|
||||||
|
|
||||||
|
public URLConnection getConnection() throws IOException {
|
||||||
|
if(c == null) c = proxy == null? new URL(url).openConnection():new URL(url).openConnection(proxy);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream open() throws IOException {
|
||||||
|
return getConnection().getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int length() throws IOException {
|
||||||
|
return getConnection().getContentLength();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Source fromURL(final Proxy proxy, final URL url) {
|
||||||
|
return new Source() {
|
||||||
|
private URLConnection c;
|
||||||
|
|
||||||
|
public URLConnection getConnection() throws IOException {
|
||||||
|
if(c == null) c = proxy == null? url.openConnection():url.openConnection(proxy);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream open() throws IOException {
|
||||||
|
return getConnection().getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int length() throws IOException {
|
||||||
|
return getConnection().getContentLength();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Destination toFile(final String file) {
|
||||||
|
return toFile(new File(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Destination toFile(final File file) {
|
||||||
|
return new Destination() {
|
||||||
|
@Override
|
||||||
|
public OutputStream open() throws IOException {
|
||||||
|
File parent = file.getAbsoluteFile().getParentFile();
|
||||||
|
//Trying to create the directory before checking it exists makes it threadsafe
|
||||||
|
if(!parent.mkdirs() && !parent.exists()) throw new IOException(parent
|
||||||
|
+ ": directory could not be created");
|
||||||
|
return new FileOutputStream(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean exists() {
|
||||||
|
return file.isFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete() throws IOException {
|
||||||
|
if(file.exists() && !file.delete()) throw new IOException(file + " couldn't be deleted");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface Source {
|
||||||
|
public InputStream open() throws IOException;
|
||||||
|
|
||||||
|
public int length() throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface Destination {
|
||||||
|
public OutputStream open() throws IOException;
|
||||||
|
|
||||||
|
public boolean exists() throws IOException;
|
||||||
|
|
||||||
|
public void delete() throws IOException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
/**
|
||||||
|
* Downloader.java
|
||||||
|
*
|
||||||
|
* Created on 25.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import javax.swing.BoundedRangeModel;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.jetlang.channels.Channel;
|
||||||
|
import org.jetlang.channels.MemoryChannel;
|
||||||
|
import org.jetlang.core.Callback;
|
||||||
|
import org.jetlang.core.Disposable;
|
||||||
|
import org.jetlang.fibers.Fiber;
|
||||||
|
import org.jetlang.fibers.PoolFiberFactory;
|
||||||
|
import org.mage.plugins.card.dl.DownloadJob.Destination;
|
||||||
|
import org.mage.plugins.card.dl.DownloadJob.Source;
|
||||||
|
import org.mage.plugins.card.dl.DownloadJob.State;
|
||||||
|
import org.mage.plugins.card.dl.lm.AbstractLaternaBean;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class Downloader.
|
||||||
|
*
|
||||||
|
* @version V0.0 25.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class Downloader extends AbstractLaternaBean implements Disposable {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger(Downloader.class);
|
||||||
|
|
||||||
|
private final List<DownloadJob> jobs = properties.list("jobs");
|
||||||
|
private final Channel<DownloadJob> channel = new MemoryChannel<DownloadJob>();
|
||||||
|
|
||||||
|
private final ExecutorService pool = Executors.newCachedThreadPool();
|
||||||
|
private final List<Fiber> fibers = new ArrayList<Fiber>();
|
||||||
|
|
||||||
|
public Downloader() {
|
||||||
|
PoolFiberFactory f = new PoolFiberFactory(pool);
|
||||||
|
//subscribe multiple fibers for parallel execution
|
||||||
|
for(int i = 0, numThreads = 10; i < numThreads; i++) {
|
||||||
|
Fiber fiber = f.create();
|
||||||
|
fiber.start();
|
||||||
|
fibers.add(fiber);
|
||||||
|
channel.subscribe(fiber, new DownloadCallback());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
for(DownloadJob j:getJobs()) {
|
||||||
|
switch(j.getState()) {
|
||||||
|
case NEW:
|
||||||
|
case WORKING:
|
||||||
|
j.setState(State.ABORTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Fiber f:fibers)
|
||||||
|
f.dispose();
|
||||||
|
pool.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
dispose();
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(DownloadJob job) {
|
||||||
|
if(job.getState() == State.WORKING) throw new IllegalArgumentException("Job already running");
|
||||||
|
if(job.getState() == State.FINISHED) throw new IllegalArgumentException("Job already finished");
|
||||||
|
job.setState(State.NEW);
|
||||||
|
jobs.add(job);
|
||||||
|
channel.publish(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DownloadJob> getJobs() {
|
||||||
|
return jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the download job: Transfers data from {@link Source} to {@link Destination} and updates the
|
||||||
|
* download job's state to reflect the progress.
|
||||||
|
*/
|
||||||
|
private class DownloadCallback implements Callback<DownloadJob> {
|
||||||
|
@Override
|
||||||
|
public void onMessage(DownloadJob job) {
|
||||||
|
//the job won't be processed by multiple threads
|
||||||
|
synchronized(job) {
|
||||||
|
if(job.getState() != State.NEW) return;
|
||||||
|
job.setState(State.WORKING);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Source src = job.getSource();
|
||||||
|
Destination dst = job.getDestination();
|
||||||
|
BoundedRangeModel progress = job.getProgress();
|
||||||
|
|
||||||
|
if(dst.exists()) {
|
||||||
|
progress.setMaximum(1);
|
||||||
|
progress.setValue(1);
|
||||||
|
} else {
|
||||||
|
progress.setMaximum(src.length());
|
||||||
|
InputStream is = new BufferedInputStream(src.open());
|
||||||
|
try {
|
||||||
|
OutputStream os = new BufferedOutputStream(dst.open());
|
||||||
|
try {
|
||||||
|
byte[] buf = new byte[8 * 1024];
|
||||||
|
int total = 0;
|
||||||
|
for(int len; (len = is.read(buf)) != -1;) {
|
||||||
|
if(job.getState() == State.ABORTED) throw new IOException("Job was aborted");
|
||||||
|
progress.setValue(total += len);
|
||||||
|
os.write(buf, 0, len);
|
||||||
|
}
|
||||||
|
} catch(IOException ex) {
|
||||||
|
try {
|
||||||
|
dst.delete();
|
||||||
|
} catch(IOException ex1) {
|
||||||
|
log.warn("While deleting", ex1);
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
os.close();
|
||||||
|
} catch(IOException ex) {
|
||||||
|
log.warn("While closing", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
is.close();
|
||||||
|
} catch(IOException ex) {
|
||||||
|
log.warn("While closing", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
job.setState(State.FINISHED);
|
||||||
|
} catch(IOException ex) {
|
||||||
|
job.setError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
* AbstractBoundBean.java
|
||||||
|
*
|
||||||
|
* Created on 24.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans;
|
||||||
|
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class AbstractBoundBean.
|
||||||
|
*
|
||||||
|
* @version V0.0 24.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public abstract class AbstractBoundBean implements BoundBean {
|
||||||
|
protected PropertyChangeSupport s = new PropertyChangeSupport(this);
|
||||||
|
|
||||||
|
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||||
|
s.addPropertyChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
|
||||||
|
s.addPropertyChangeListener(propertyName, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
||||||
|
s.removePropertyChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
|
||||||
|
s.removePropertyChangeListener(propertyName, listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* BoundBean.java
|
||||||
|
*
|
||||||
|
* Created on 24.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans;
|
||||||
|
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class BoundBean.
|
||||||
|
*
|
||||||
|
* @version V0.0 24.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public interface BoundBean {
|
||||||
|
public void addPropertyChangeListener(PropertyChangeListener listener);
|
||||||
|
|
||||||
|
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener);
|
||||||
|
|
||||||
|
public void removePropertyChangeListener(PropertyChangeListener listener);
|
||||||
|
|
||||||
|
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
/**
|
||||||
|
* EventListenerList.java
|
||||||
|
*
|
||||||
|
* Created on 08.04.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans;
|
||||||
|
|
||||||
|
|
||||||
|
import static java.util.Arrays.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EventListener;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.collect.AbstractIterator;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class EventListenerList.
|
||||||
|
*
|
||||||
|
* @version V0.0 08.04.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class EventListenerList extends javax.swing.event.EventListenerList {
|
||||||
|
private static final long serialVersionUID = -7545754245081842909L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterable over all listeners for the specified classes. the listener classes are in the specified
|
||||||
|
* order. for every class, listeners are in the reverse order of registering. A listener contained multiple
|
||||||
|
* times (for a single or multiple classes) is only returned the first time it occurs.
|
||||||
|
*/
|
||||||
|
public <T extends EventListener> Iterable<T> getIterable(final Class<? extends T>... listenerClass) {
|
||||||
|
//transform class -> iterable
|
||||||
|
List<Iterable<T>> l = Lists.transform(asList(listenerClass), new ClassToIterableFunction<T>());
|
||||||
|
|
||||||
|
//compose iterable (use an arraylist to memoize the function's results)
|
||||||
|
final Iterable<T> it = Iterables.concat(new ArrayList<Iterable<T>>(l));
|
||||||
|
|
||||||
|
//transform to singleton iterators
|
||||||
|
return new Iterable<T>() {
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return new SingletonIterator<T>(it.iterator());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator over all listeners for the specified classes. the listener classes are in the specified
|
||||||
|
* order. for every class, listeners are in the reverse order of registering. A listener contained multiple
|
||||||
|
* times (for a single or multiple classes) is only returned the first time it occurs.
|
||||||
|
*/
|
||||||
|
public <T extends EventListener> Iterator<T> getIterator(Class<? extends T>... listenerClass) {
|
||||||
|
return getIterable(listenerClass).iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates backwards over the listeners registered for a class by using the original array. The Listener runs
|
||||||
|
* backwards, just as listener notification usually works.
|
||||||
|
*/
|
||||||
|
private class ListenerIterator<T> extends AbstractIterator<T> {
|
||||||
|
private final Class<? extends T> listenerClass;
|
||||||
|
private Object[] listeners = listenerList;
|
||||||
|
private int index = listeners.length;
|
||||||
|
|
||||||
|
private ListenerIterator(Class<? extends T> listenerClass) {
|
||||||
|
this.listenerClass = listenerClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected T computeNext() {
|
||||||
|
for(index -= 2; index >= 0; index -= 2) {
|
||||||
|
if(listenerClass == listeners[index]) return (T) listeners[index + 1];
|
||||||
|
}
|
||||||
|
return endOfData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a class to the associated listener iterator
|
||||||
|
*/
|
||||||
|
private class ClassToIterableFunction<T> implements Function<Class<? extends T>, Iterable<T>> {
|
||||||
|
|
||||||
|
public Iterable<T> apply(final Class<? extends T> from) {
|
||||||
|
return new Iterable<T>() {
|
||||||
|
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return new ListenerIterator<T>(from);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the delegate iterator so that every but the first occurrence of every element is ignored.
|
||||||
|
*/
|
||||||
|
private static class SingletonIterator<T> extends AbstractIterator<T> {
|
||||||
|
private Iterator<T> it;
|
||||||
|
private HashSet<T> previous = new HashSet<T>();
|
||||||
|
|
||||||
|
public SingletonIterator(Iterator<T> it) {
|
||||||
|
this.it = it;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T computeNext() {
|
||||||
|
while(it.hasNext()) {
|
||||||
|
T next = it.next();
|
||||||
|
if(previous.add(next)) return next;
|
||||||
|
}
|
||||||
|
return endOfData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* PropertyChangeSupport.java
|
||||||
|
*
|
||||||
|
* Created on 16.07.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class PropertyChangeSupport.
|
||||||
|
*
|
||||||
|
* @version V0.0 16.07.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class PropertyChangeSupport extends java.beans.PropertyChangeSupport {
|
||||||
|
private static final long serialVersionUID = -4241465377828790076L;
|
||||||
|
|
||||||
|
private Object sourceBean;
|
||||||
|
|
||||||
|
public PropertyChangeSupport(Object sourceBean) {
|
||||||
|
super(sourceBean);
|
||||||
|
this.sourceBean = sourceBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getSourceBean() {
|
||||||
|
return sourceBean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
/**
|
||||||
|
* This package contains bean-related utilities, most importantly
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link net.slightlymagic.beans.BoundBean} and {@link net.slightlymagic.beans.AbstractBoundBean}, an interface and an
|
||||||
|
* abstract class for easier property change support for bean classes</li>
|
||||||
|
* <li>{@link net.slightlymagic.beans.properties.Properties} and its implementations. These make it easy for beans
|
||||||
|
* to use delegates, by providing a centralized configuration for bean properties. For example, a {@link
|
||||||
|
* net.slightlymagic.beans.properties.bound.BoundProperties} object creates properties that do automatic property
|
||||||
|
* change notifications. What exact configuration is used is thus hidden from the delegate.</li>
|
||||||
|
* <li>The {@link net.slightlymagic.beans.relational.Relations} class provides relational properties that model
|
||||||
|
* bidirectional 1:1, 1:n and m:n relationships.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* AbstractProperties.java
|
||||||
|
*
|
||||||
|
* Created on 24.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans.properties;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.mage.plugins.card.dl.beans.properties.basic.BasicProperty;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class AbstractProperties.
|
||||||
|
*
|
||||||
|
* @version V0.0 24.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public abstract class AbstractProperties implements Properties {
|
||||||
|
public <T> Property<T> property(String name, T value) {
|
||||||
|
return property(name, new BasicProperty<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Property<T> property(String name) {
|
||||||
|
return property(name, new BasicProperty<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E> List<E> list(String name) {
|
||||||
|
return list(name, new ArrayList<E>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E> Set<E> set(String name) {
|
||||||
|
return set(name, new HashSet<E>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <K, V> Map<K, V> map(String name) {
|
||||||
|
return map(name, new HashMap<K, V>());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* AbstractProperty.java
|
||||||
|
*
|
||||||
|
* Created on 24.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans.properties;
|
||||||
|
|
||||||
|
|
||||||
|
import static java.lang.String.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class AbstractProperty.
|
||||||
|
*
|
||||||
|
* @version V0.0 24.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public abstract class AbstractProperty<T> implements Property<T> {
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
T value = getValue();
|
||||||
|
return value == null? 0:value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if(!(obj instanceof Property<?>)) return false;
|
||||||
|
Object value = getValue();
|
||||||
|
Object other = ((Property<?>) obj).getValue();
|
||||||
|
return value == other || (value != null && value.equals(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return valueOf(getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
* CompoundProperties.java
|
||||||
|
*
|
||||||
|
* Created on 24.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans.properties;
|
||||||
|
|
||||||
|
|
||||||
|
import static java.util.Arrays.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class CompoundProperties.
|
||||||
|
*
|
||||||
|
* @version V0.0 24.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class CompoundProperties extends AbstractProperties {
|
||||||
|
private List<Properties> delegates;
|
||||||
|
|
||||||
|
public CompoundProperties(Properties... delegates) {
|
||||||
|
this.delegates = asList(delegates);
|
||||||
|
Collections.reverse(this.delegates);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundProperties(List<Properties> delegates) {
|
||||||
|
this.delegates = new ArrayList<Properties>(delegates);
|
||||||
|
Collections.reverse(this.delegates);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Property<T> property(String name, Property<T> property) {
|
||||||
|
for(Properties p:delegates)
|
||||||
|
property = p.property(name, property);
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E> List<E> list(String name, List<E> list) {
|
||||||
|
for(Properties p:delegates)
|
||||||
|
list = p.list(name, list);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E> Set<E> set(String name, Set<E> set) {
|
||||||
|
for(Properties p:delegates)
|
||||||
|
set = p.set(name, set);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <K, V> Map<K, V> map(String name, Map<K, V> map) {
|
||||||
|
for(Properties p:delegates)
|
||||||
|
map = p.map(name, map);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
* Properties.java
|
||||||
|
*
|
||||||
|
* Created on 24.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans.properties;
|
||||||
|
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.mage.plugins.card.dl.beans.properties.bound.BoundProperties;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class Properties. A Properties object is a factory for bean properties. For example, the
|
||||||
|
* {@link BoundProperties} class produces properties that can be observed using {@link PropertyChangeListener}s.
|
||||||
|
*
|
||||||
|
* @version V0.0 24.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public interface Properties {
|
||||||
|
public <T> Property<T> property(String name, Property<T> property);
|
||||||
|
|
||||||
|
public <E> List<E> list(String name, List<E> list);
|
||||||
|
|
||||||
|
public <E> Set<E> set(String name, Set<E> set);
|
||||||
|
|
||||||
|
public <K, V> Map<K, V> map(String name, Map<K, V> map);
|
||||||
|
|
||||||
|
|
||||||
|
public <T> Property<T> property(String name, T value);
|
||||||
|
|
||||||
|
public <T> Property<T> property(String name);
|
||||||
|
|
||||||
|
public <E> List<E> list(String name);
|
||||||
|
|
||||||
|
public <E> Set<E> set(String name);
|
||||||
|
|
||||||
|
public <K, V> Map<K, V> map(String name);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Property.java
|
||||||
|
*
|
||||||
|
* Created on 24.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans.properties;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class Property.
|
||||||
|
*
|
||||||
|
* @version V0.0 24.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public interface Property<T> {
|
||||||
|
public void setValue(T value);
|
||||||
|
|
||||||
|
public T getValue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A property's hash code is its value's hashCode, or {@code null} if the value is null.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Two properties are equal if their values are equal.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
* BoundProperties.java
|
||||||
|
*
|
||||||
|
* Created on 24.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans.properties.basic;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.mage.plugins.card.dl.beans.properties.AbstractProperties;
|
||||||
|
import org.mage.plugins.card.dl.beans.properties.Property;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class BoundProperties.
|
||||||
|
*
|
||||||
|
* @version V0.0 24.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class BasicProperties extends AbstractProperties {
|
||||||
|
public <T> Property<T> property(String name, Property<T> value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E> List<E> list(String name, List<E> list) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E> Set<E> set(String name, Set<E> set) {
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <K, V> Map<K, V> map(String name, Map<K, V> map) {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
* BasicProperty.java
|
||||||
|
*
|
||||||
|
* Created on 16.07.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans.properties.basic;
|
||||||
|
|
||||||
|
|
||||||
|
import org.mage.plugins.card.dl.beans.properties.AbstractProperty;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class BasicProperty.
|
||||||
|
*
|
||||||
|
* @version V0.0 16.07.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class BasicProperty<T> extends AbstractProperty<T> {
|
||||||
|
private T value;
|
||||||
|
|
||||||
|
public BasicProperty() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasicProperty(T initialValue) {
|
||||||
|
value = initialValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(T value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
* BoundProperties.java
|
||||||
|
*
|
||||||
|
* Created on 24.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans.properties.bound;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.mage.plugins.card.dl.beans.collections.ListenableCollections.listenableList;
|
||||||
|
import static org.mage.plugins.card.dl.beans.collections.ListenableCollections.listenableMap;
|
||||||
|
import static org.mage.plugins.card.dl.beans.collections.ListenableCollections.listenableSet;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.mage.plugins.card.dl.beans.PropertyChangeSupport;
|
||||||
|
import org.mage.plugins.card.dl.beans.properties.AbstractProperties;
|
||||||
|
import org.mage.plugins.card.dl.beans.properties.Property;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class BoundProperties.
|
||||||
|
*
|
||||||
|
* @version V0.0 24.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class BoundProperties extends AbstractProperties {
|
||||||
|
public final PropertyChangeSupport s;
|
||||||
|
|
||||||
|
public BoundProperties(Object sourceBean) {
|
||||||
|
this(new PropertyChangeSupport(sourceBean));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundProperties(PropertyChangeSupport s) {
|
||||||
|
if(s == null) throw new IllegalArgumentException("s == null");
|
||||||
|
this.s = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Property<T> property(String name, Property<T> property) {
|
||||||
|
return new BoundProperty<T>(s, name, property);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E> List<E> list(String name, List<E> list) {
|
||||||
|
return listenableList(list, new PropertyChangeListListener<E>(s, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E> Set<E> set(String name, Set<E> set) {
|
||||||
|
return listenableSet(set, new PropertyChangeSetListener<E>(s, set, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <K, V> Map<K, V> map(String name, Map<K, V> map) {
|
||||||
|
return listenableMap(map, new PropertyChangeMapListener<K, V>(s, map, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
* BasicProperty.java
|
||||||
|
*
|
||||||
|
* Created on 16.07.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans.properties.bound;
|
||||||
|
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeSupport;
|
||||||
|
|
||||||
|
import org.mage.plugins.card.dl.beans.properties.AbstractProperty;
|
||||||
|
import org.mage.plugins.card.dl.beans.properties.Property;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class BasicProperty.
|
||||||
|
*
|
||||||
|
* @version V0.0 16.07.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class BoundProperty<T> extends AbstractProperty<T> {
|
||||||
|
private PropertyChangeSupport s;
|
||||||
|
private String name;
|
||||||
|
private Property<T> property;
|
||||||
|
|
||||||
|
public BoundProperty(PropertyChangeSupport s, String name, Property<T> property) {
|
||||||
|
if(property == null) throw new IllegalArgumentException();
|
||||||
|
this.s = s;
|
||||||
|
this.name = name;
|
||||||
|
this.property = property;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(T value) {
|
||||||
|
T old = getValue();
|
||||||
|
property.setValue(value);
|
||||||
|
s.firePropertyChange(name, old, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getValue() {
|
||||||
|
return property.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
* PropertyChangeListListener.java
|
||||||
|
*
|
||||||
|
* Created on 16.07.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans.properties.bound;
|
||||||
|
|
||||||
|
import org.mage.plugins.card.dl.beans.PropertyChangeSupport;
|
||||||
|
import org.mage.plugins.card.dl.beans.collections.ListenableCollections.ListListener;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class PropertyChangeListListener.
|
||||||
|
*
|
||||||
|
* @version V0.0 16.07.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class PropertyChangeListListener<E> implements ListListener<E> {
|
||||||
|
private static final long serialVersionUID = 625853864429729560L;
|
||||||
|
|
||||||
|
private PropertyChangeSupport s;
|
||||||
|
private String propertyName;
|
||||||
|
|
||||||
|
public PropertyChangeListListener(PropertyChangeSupport s, String propertyName) {
|
||||||
|
this.s = s;
|
||||||
|
this.propertyName = propertyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(int index, E newValue) {
|
||||||
|
s.fireIndexedPropertyChange(propertyName, index, null, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int index, E oldValue, E newValue) {
|
||||||
|
s.fireIndexedPropertyChange(propertyName, index, oldValue, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(int index, E oldValue) {
|
||||||
|
s.fireIndexedPropertyChange(propertyName, index, oldValue, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
/**
|
||||||
|
* PropertyChangeMapListener.java
|
||||||
|
*
|
||||||
|
* Created on 16.07.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans.properties.bound;
|
||||||
|
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.mage.plugins.card.dl.beans.PropertyChangeSupport;
|
||||||
|
import org.mage.plugins.card.dl.beans.collections.ListenableCollections.MapListener;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class PropertyChangeMapListener. This listener alway fires events with {@link Map} -> {@link Map} as the
|
||||||
|
* value parameters, as nonindexed collection properties. The events will be {@link MapEvent} instances.
|
||||||
|
*
|
||||||
|
* @version V0.0 16.07.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class PropertyChangeMapListener<K, V> implements MapListener<K, V> {
|
||||||
|
private static final long serialVersionUID = 625853864429729560L;
|
||||||
|
|
||||||
|
private PropertyChangeSupport s;
|
||||||
|
private Map<K, V> map;
|
||||||
|
private String propertyName;
|
||||||
|
|
||||||
|
public PropertyChangeMapListener(PropertyChangeSupport s, Map<K, V> map, String propertyName) {
|
||||||
|
this.s = s;
|
||||||
|
this.map = map;
|
||||||
|
this.propertyName = propertyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(K key, V newValue) {
|
||||||
|
s.firePropertyChange(new MapPutEvent<K, V>(s.getSourceBean(), propertyName, map, key, newValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(K key, V oldValue, V newValue) {
|
||||||
|
s.firePropertyChange(new MapSetEvent<K, V>(s.getSourceBean(), propertyName, map, key, oldValue, newValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(K key, V oldValue) {
|
||||||
|
s.firePropertyChange(new MapRemoveEvent<K, V>(s.getSourceBean(), propertyName, map, key, oldValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class MapEvent<K, V> extends PropertyChangeEvent {
|
||||||
|
private static final long serialVersionUID = -651568020675693544L;
|
||||||
|
|
||||||
|
private Map<K, V> map;
|
||||||
|
private K key;
|
||||||
|
|
||||||
|
public MapEvent(Object source, String propertyName, Map<K, V> map, K key) {
|
||||||
|
super(source, propertyName, null, null);
|
||||||
|
this.map = map;
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<K, V> getOldValue() {
|
||||||
|
//old and new value must not be equal
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<K, V> getNewValue() {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public K getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MapPutEvent<K, V> extends MapEvent<K, V> {
|
||||||
|
private static final long serialVersionUID = 6006291474676939650L;
|
||||||
|
|
||||||
|
private V newElement;
|
||||||
|
|
||||||
|
public MapPutEvent(Object source, String propertyName, Map<K, V> map, K key, V newElement) {
|
||||||
|
super(source, propertyName, map, key);
|
||||||
|
this.newElement = newElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public V getNewElement() {
|
||||||
|
return newElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MapSetEvent<K, V> extends MapEvent<K, V> {
|
||||||
|
private static final long serialVersionUID = -2419438379909500079L;
|
||||||
|
|
||||||
|
private V oldElement, newElement;
|
||||||
|
|
||||||
|
public MapSetEvent(Object source, String propertyName, Map<K, V> map, K key, V oldElement, V newElement) {
|
||||||
|
super(source, propertyName, map, key);
|
||||||
|
this.oldElement = oldElement;
|
||||||
|
this.newElement = newElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public V getOldElement() {
|
||||||
|
return oldElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public V getNewElement() {
|
||||||
|
return newElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MapRemoveEvent<K, V> extends MapEvent<K, V> {
|
||||||
|
private static final long serialVersionUID = -2644879706878221895L;
|
||||||
|
|
||||||
|
private V oldElement;
|
||||||
|
|
||||||
|
public MapRemoveEvent(Object source, String propertyName, Map<K, V> map, K key, V oldElement) {
|
||||||
|
super(source, propertyName, map, key);
|
||||||
|
this.oldElement = oldElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public V getOldElement() {
|
||||||
|
return oldElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* PropertyChangeSetListener.java
|
||||||
|
*
|
||||||
|
* Created on 16.07.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.beans.properties.bound;
|
||||||
|
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.mage.plugins.card.dl.beans.PropertyChangeSupport;
|
||||||
|
import org.mage.plugins.card.dl.beans.collections.ListenableCollections.SetListener;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class PropertyChangeSetListener. This listener always fires events with {@link Set} -> {@link Set} as the
|
||||||
|
* value parameters, as nonindexed collection properties. The events will be {@link SetEvent} instances.
|
||||||
|
*
|
||||||
|
* @version V0.0 16.07.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class PropertyChangeSetListener<E> implements SetListener<E> {
|
||||||
|
private static final long serialVersionUID = 625853864429729560L;
|
||||||
|
|
||||||
|
private PropertyChangeSupport s;
|
||||||
|
private Set<E> set;
|
||||||
|
private String propertyName;
|
||||||
|
|
||||||
|
public PropertyChangeSetListener(PropertyChangeSupport s, Set<E> set, String propertyName) {
|
||||||
|
this.s = s;
|
||||||
|
this.set = set;
|
||||||
|
this.propertyName = propertyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(E newValue) {
|
||||||
|
s.firePropertyChange(new SetAddEvent<E>(s.getSourceBean(), propertyName, set, newValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(E oldValue) {
|
||||||
|
s.firePropertyChange(new SetRemoveEvent<E>(s.getSourceBean(), propertyName, set, oldValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class SetEvent<E> extends PropertyChangeEvent {
|
||||||
|
private static final long serialVersionUID = -651568020675693544L;
|
||||||
|
|
||||||
|
private Set<E> set;
|
||||||
|
|
||||||
|
public SetEvent(Object source, String propertyName, Set<E> set) {
|
||||||
|
super(source, propertyName, null, null);
|
||||||
|
this.set = set;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<E> getOldValue() {
|
||||||
|
//old and new value must not be equal
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<E> getNewValue() {
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SetAddEvent<E> extends SetEvent<E> {
|
||||||
|
private static final long serialVersionUID = 9041766866796759871L;
|
||||||
|
|
||||||
|
private E newElement;
|
||||||
|
|
||||||
|
public SetAddEvent(Object source, String propertyName, Set<E> set, E newElement) {
|
||||||
|
super(source, propertyName, set);
|
||||||
|
this.newElement = newElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public E getNewElement() {
|
||||||
|
return newElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SetRemoveEvent<E> extends SetEvent<E> {
|
||||||
|
private static final long serialVersionUID = -1315342339926392385L;
|
||||||
|
|
||||||
|
private E oldElement;
|
||||||
|
|
||||||
|
public SetRemoveEvent(Object source, String propertyName, Set<E> set, E oldElement) {
|
||||||
|
super(source, propertyName, set);
|
||||||
|
this.oldElement = oldElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public E getOldElement() {
|
||||||
|
return oldElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* AbstractLaternaBean.java
|
||||||
|
*
|
||||||
|
* Created on 25.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.lm;
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.mage.plugins.card.dl.beans.AbstractBoundBean;
|
||||||
|
import org.mage.plugins.card.dl.beans.EventListenerList;
|
||||||
|
import org.mage.plugins.card.dl.beans.properties.Properties;
|
||||||
|
import org.mage.plugins.card.dl.beans.properties.bound.BoundProperties;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class AbstractLaternaBean.
|
||||||
|
*
|
||||||
|
* @version V0.0 25.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class AbstractLaternaBean extends AbstractBoundBean {
|
||||||
|
protected static final Logger log = Logger.getLogger(AbstractLaternaBean.class);
|
||||||
|
protected Properties properties = new BoundProperties(s);
|
||||||
|
protected EventListenerList listeners = new EventListenerList();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
* GathererSymbols.java
|
||||||
|
*
|
||||||
|
* Created on 25.08.2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mage.plugins.card.dl.sources;
|
||||||
|
|
||||||
|
|
||||||
|
import static java.lang.String.format;
|
||||||
|
import static org.mage.plugins.card.dl.DownloadJob.fromURL;
|
||||||
|
import static org.mage.plugins.card.dl.DownloadJob.toFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.mage.plugins.card.dl.DownloadJob;
|
||||||
|
|
||||||
|
import com.google.common.collect.AbstractIterator;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class GathererSymbols.
|
||||||
|
*
|
||||||
|
* @version V0.0 25.08.2010
|
||||||
|
* @author Clemens Koza
|
||||||
|
*/
|
||||||
|
public class GathererSymbols implements Iterable<DownloadJob> {
|
||||||
|
//TODO chaos and planeswalker symbol
|
||||||
|
//chaos: http://gatherer.wizards.com/Images/Symbols/chaos.gif
|
||||||
|
|
||||||
|
private static final File outDir = new File("plugins/images/symbols");
|
||||||
|
private static final String urlFmt = "http://gatherer.wizards.com/handlers/image.ashx?size=%1$s&name=%2$s&type=symbol";
|
||||||
|
|
||||||
|
private static final String[] sizes = {"small", "medium", "large"};
|
||||||
|
|
||||||
|
private static final String[] symbols = {"W", "U", "B", "R", "G",
|
||||||
|
|
||||||
|
"W/U", "U/B", "B/R", "R/G", "G/W", "W/B", "U/R", "B/G", "R/W", "G/U",
|
||||||
|
|
||||||
|
"2/W", "2/U", "2/B", "2/R", "2/G",
|
||||||
|
|
||||||
|
"X", "S", "T", "Q"};
|
||||||
|
private static final int minNumeric = 0, maxNumeric = 16;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<DownloadJob> iterator() {
|
||||||
|
return new AbstractIterator<DownloadJob>() {
|
||||||
|
private int sizeIndex, symIndex, numeric = minNumeric;
|
||||||
|
private File dir = new File(outDir, sizes[sizeIndex]);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DownloadJob computeNext() {
|
||||||
|
String sym;
|
||||||
|
if(symIndex < symbols.length) {
|
||||||
|
sym = symbols[symIndex++];
|
||||||
|
} else if(numeric <= maxNumeric) {
|
||||||
|
sym = "" + (numeric++);
|
||||||
|
} else {
|
||||||
|
sizeIndex++;
|
||||||
|
if(sizeIndex == sizes.length) return endOfData();
|
||||||
|
|
||||||
|
symIndex = 0;
|
||||||
|
numeric = 0;
|
||||||
|
dir = new File(outDir, sizes[sizeIndex]);
|
||||||
|
return computeNext();
|
||||||
|
}
|
||||||
|
String symbol = sym.replaceAll("/", "");
|
||||||
|
File dst = new File(dir, symbol + ".jpg");
|
||||||
|
|
||||||
|
if(symbol.equals("T")) symbol = "tap";
|
||||||
|
else if(symbol.equals("Q")) symbol = "untap";
|
||||||
|
else if(symbol.equals("S")) symbol = "snow";
|
||||||
|
|
||||||
|
String url = format(urlFmt, sizes[sizeIndex], symbol);
|
||||||
|
|
||||||
|
return new DownloadJob(sym, fromURL(url), toFile(dst));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
package org.mage.plugins.card.images;
|
package org.mage.plugins.card.images;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Frame;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.WindowAdapter;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
@ -42,6 +46,10 @@ import mage.cards.Card;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.mage.plugins.card.CardUrl;
|
import org.mage.plugins.card.CardUrl;
|
||||||
import org.mage.plugins.card.constants.Constants;
|
import org.mage.plugins.card.constants.Constants;
|
||||||
|
import org.mage.plugins.card.dl.DownloadGui;
|
||||||
|
import org.mage.plugins.card.dl.DownloadJob;
|
||||||
|
import org.mage.plugins.card.dl.Downloader;
|
||||||
|
import org.mage.plugins.card.dl.sources.GathererSymbols;
|
||||||
import org.mage.plugins.card.properties.SettingsManager;
|
import org.mage.plugins.card.properties.SettingsManager;
|
||||||
import org.mage.plugins.card.utils.CardImageUtils;
|
import org.mage.plugins.card.utils.CardImageUtils;
|
||||||
|
|
||||||
|
|
@ -60,6 +68,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
||||||
private JLabel jLabel1;
|
private JLabel jLabel1;
|
||||||
private static boolean offlineMode = false;
|
private static boolean offlineMode = false;
|
||||||
private JCheckBox checkBox;
|
private JCheckBox checkBox;
|
||||||
|
private JButton downloadSymbols;
|
||||||
|
|
||||||
public static final Proxy.Type[] types = Proxy.Type.values();
|
public static final Proxy.Type[] types = Proxy.Type.values();
|
||||||
|
|
||||||
|
|
@ -70,10 +79,10 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
||||||
public static void startDownload(JFrame frame, Set<Card> allCards) {
|
public static void startDownload(JFrame frame, Set<Card> allCards) {
|
||||||
ArrayList<CardUrl> cards = getNeededCards(allCards);
|
ArrayList<CardUrl> cards = getNeededCards(allCards);
|
||||||
|
|
||||||
if (cards == null || cards.size() == 0) {
|
/*if (cards == null || cards.size() == 0) {
|
||||||
JOptionPane.showMessageDialog(null, "All card pictures have been downloaded.");
|
JOptionPane.showMessageDialog(null, "All card pictures have been downloaded.");
|
||||||
return;
|
return;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
DownloadPictures download = new DownloadPictures(cards);
|
DownloadPictures download = new DownloadPictures(cards);
|
||||||
JDialog dlg = download.getDlg(frame);
|
JDialog dlg = download.getDlg(frame);
|
||||||
|
|
@ -147,7 +156,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
||||||
jComboBox1.setAlignmentX(Component.LEFT_ALIGNMENT);
|
jComboBox1.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
p0.add(jComboBox1);
|
p0.add(jComboBox1);
|
||||||
p0.add(Box.createVerticalStrut(5));
|
p0.add(Box.createVerticalStrut(5));
|
||||||
|
|
||||||
// Start
|
// Start
|
||||||
final JButton b = new JButton("Start download");
|
final JButton b = new JButton("Start download");
|
||||||
b.addActionListener(new ActionListener() {
|
b.addActionListener(new ActionListener() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package org.mage.plugins.card.utils;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.ImageObserver;
|
||||||
|
|
||||||
|
public class BufferedImageBuilder {
|
||||||
|
|
||||||
|
private static final int DEFAULT_IMAGE_TYPE = BufferedImage.TYPE_INT_RGB;
|
||||||
|
|
||||||
|
public BufferedImage bufferImage(Image image) {
|
||||||
|
return bufferImage(image, DEFAULT_IMAGE_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferedImage bufferImage(Image image, int type) {
|
||||||
|
BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
|
||||||
|
Graphics2D g = bufferedImage.createGraphics();
|
||||||
|
g.drawImage(image, null, null);
|
||||||
|
//waitForImage(bufferedImage);
|
||||||
|
return bufferedImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForImage(BufferedImage bufferedImage) {
|
||||||
|
final ImageLoadStatus imageLoadStatus = new ImageLoadStatus();
|
||||||
|
bufferedImage.getHeight(new ImageObserver() {
|
||||||
|
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
|
||||||
|
if (infoflags == ALLBITS) {
|
||||||
|
imageLoadStatus.heightDone = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
bufferedImage.getWidth(new ImageObserver() {
|
||||||
|
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
|
||||||
|
if (infoflags == ALLBITS) {
|
||||||
|
imageLoadStatus.widthDone = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
while (!imageLoadStatus.widthDone && !imageLoadStatus.heightDone) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(300);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImageLoadStatus {
|
||||||
|
|
||||||
|
public boolean widthDone = false;
|
||||||
|
public boolean heightDone = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
5
pom.xml
5
pom.xml
|
|
@ -36,6 +36,11 @@
|
||||||
<name>Java Image Scaling Repository Released</name>
|
<name>Java Image Scaling Repository Released</name>
|
||||||
<url>svn:https://java-image-scaling.googlecode.com/svn/repo/released</url>
|
<url>svn:https://java-image-scaling.googlecode.com/svn/repo/released</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>jetlang.googlecode.com</id>
|
||||||
|
<name>Jetlang Repository for Maven</name>
|
||||||
|
<url>http://jetlang.googlecode.com/svn/repo/</url>
|
||||||
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue