/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.tools;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.LinearGradientPaint;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import javax.imageio.ImageIO;
/**
* Creates dynamic icons based on the hostname and the url path elements. Before you can use any
* custom dynamic icons, register them by calling {@link #registerDynamicIcon(String, DynamicIcon)}. <br/>
* Use {@link #IDENTIFIER_PROGRESS} or {@link #IDENTIFIER_QUOTA} for existing dynamic icon
* implementations.
* <p>
* Usage: <br/>
* If the identifier is <code>progress</code>, the HTML icon tag would look like this:
* </p>
* <p>
* <code><img src=\"dynicon://progress/100/14/50></code>
* </p>
* <p>
* where the first number after the identifier is the width of the icon in px, the second number is
* the height in px and the third number is the percentage (0-100).
* </p>
*
* @author Thilo Kamradt, Simon Fischer, Marco Boeck
*
*/
public class DynamicIconUrlStreamHandler extends URLStreamHandler {
/** the identifier of the progress bar icon (fading from green to very dark green) */
public static final String IDENTIFIER_PROGRESS = "progress";
/** the identifier of the quota bar icon (first 50% green, last 50% fading from green to red) */
public static final String IDENTIFIER_QUOTA = "quota";
/**
* the identifier of the quota bar icon (always one color only: green (0-70%), orange (70-90%),
* red (90-100%)
*/
public static final String IDENTIFIER_QUOTA_UNI = "quota_uni";
/** mapping between identifiers and the dynicon implementations */
private static final Map<String, DynamicIcon> mapping = new HashMap<>();
static {
// register default implementation o
mapping.put(IDENTIFIER_PROGRESS, new DynamicIcon() {
@Override
public void drawIcon(Graphics2D g2, int width, int height, int percentage) {
g2.setColor(Color.WHITE);
g2.setPaint(new GradientPaint(0, 0, Color.WHITE.darker(), 0, (float) (height * 0.5), Color.WHITE, true));
g2.fillRect(0, 0, width, height);
g2.setColor(Color.GREEN);
g2.setPaint(new GradientPaint(0, 0, Color.GREEN.darker(), percentage, (float) (height * 0.5), Color.GREEN
.darker().darker(), false));
g2.fillRect(0, 0, (int) (percentage * 200d / 100d), height);
g2.setColor(Color.BLACK);
g2.drawRect(0, 0, width - 1, height - 1);
g2.dispose();
}
});
mapping.put(IDENTIFIER_QUOTA, new DynamicIcon() {
@Override
public void drawIcon(Graphics2D g2, int width, int height, int percentage) {
// prepare background
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, width, height);
// 0-50 % is the same color, only the last 50% fade into red
Color startColor = new Color(0f, .5f, 0, 0.7f);
g2.setColor(startColor);
g2.setPaint(new LinearGradientPaint(width / 2, 0f, width, 0f, new float[] { 0f, 0.5f, 1f }, new Color[] {
new Color(0f, .5f, 0, 0.7f), new Color(1f, 1f, 0f, 0.7f), new Color(1f, 0, 0, 0.7f) }));
g2.fillRect(0, 0, (int) (percentage / 100d * width), height);
// draw border
g2.setColor(Color.GRAY);
g2.drawRect(0, 0, width - 1, height - 1);
g2.dispose();
}
});
mapping.put(IDENTIFIER_QUOTA_UNI, new DynamicIcon() {
@Override
public void drawIcon(Graphics2D g2, int width, int height, int percentage) {
// prepare background
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, width, height);
// 0-70 % is the green color; 70-90% is gradient orange; 90-100% is red
float red = 0.0f;
float green = 0.0f;
float blue = 0.0f;
float percentageFloat = percentage / 100f;
if (percentage < 70) {
green = 0.75f;
} else if (percentage >= 70 && percentage < 90) {
red = 1.0f;
green = 1.0f - 2 * (percentageFloat - 0.5f);
} else if (percentage >= 90) {
red = 1.0f;
}
Color fillColor = new Color(red, green, blue);
g2.setColor(fillColor);
g2.fillRect(0, 0, (int) (percentage / 100d * width), height);
// draw border
g2.setColor(Color.GRAY);
g2.drawRect(0, 0, width - 1, height - 1);
g2.dispose();
}
});
}
/**
* Register the specified {@link DynamicIcon} under the given identifier. For instructions on
* how to use the icon in an HTML document, see {@link DynamicIcon}.
*
* @param identifier
* the identifier of the dynamic icon
* @param dynIcon
* the implementation of the icon
*/
public static void registerDynamicIcon(String identifier, DynamicIcon dynIcon) {
if (identifier == null || identifier.isEmpty()) {
throw new IllegalArgumentException("identifier must not be null or empty!");
}
if (dynIcon == null) {
throw new IllegalArgumentException("dynIcon must not be null!");
}
if (IDENTIFIER_PROGRESS.equals(identifier) || IDENTIFIER_QUOTA.equals(identifier)
|| IDENTIFIER_QUOTA_UNI.equals(identifier)) {
throw new IllegalArgumentException("cannot override default implementations");
}
mapping.put(identifier, dynIcon);
}
@Override
protected URLConnection openConnection(final URL u) throws IOException {
return new URLConnection(u) {
@Override
public InputStream getInputStream() throws IOException {
String[] parameter = u.getFile().substring(1).split("/");
int width = Integer.parseInt(parameter[0]);
int height = Integer.parseInt(parameter[1]);
int percentage = Integer.parseInt(parameter[2]);
// make sure parameters are valid, otherwise throw
if (width <= 0) {
throw new IllegalArgumentException("dynicon width must not be <= 0");
}
if (height <= 0) {
throw new IllegalArgumentException("dynicon height must not be <= 0");
}
if (percentage < 0 || percentage > 100) {
throw new IllegalArgumentException("dynicon percentage must not be < 0 or > 100");
}
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
Graphics2D g2 = (Graphics2D) img.getGraphics();
String type = u.getHost();
// check if we have a registered implementation and use it
boolean found = false;
for (String key : mapping.keySet()) {
if (key.equals(type)) {
mapping.get(key).drawIcon(g2, width, height, percentage);
found = true;
break;
}
}
// unknown dynicon type
if (!found) {
throw new IOException("Unknown dynamic icon type: " + type);
}
// create image from graphics and return it
try {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ImageIO.write(img, "png", buffer);
buffer.close();
return new ByteArrayInputStream(buffer.toByteArray());
} catch (IOException e) {
LogService.getRoot()
.log(Level.WARNING, "com.rapidminer.tools.DynamicIconUrlStreamHandler.icon_error", e);
e.printStackTrace();
return null;
}
}
@Override
public void connect() throws IOException {
// no-op
}
};
}
}