[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:
magenoxx 2010-11-24 08:20:46 +00:00
parent 7d9d2bc744
commit c650c43f94
35 changed files with 1758 additions and 25 deletions

View file

@ -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>

View file

@ -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);
@ -174,6 +189,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();
this.tablesPane.setVisible(false); this.tablesPane.setVisible(false);

View file

@ -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();
} }

View file

@ -113,6 +113,11 @@ public class Plugins implements MagePlugins {
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() {
if (this.counterPlugin != null) { if (this.counterPlugin != null) {

View file

@ -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();
} }

View file

@ -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>

View file

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

View file

@ -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, " ");

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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();
}
}
}

View file

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

View file

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

View file

@ -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>());
}
}

View file

@ -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());
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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();
}
}

View file

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

View file

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

View file

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

View file

@ -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();
}

View file

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

View file

@ -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);

View file

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

View file

@ -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>