/*
* Copyright 2000-2016 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.ui.declarative.converters;
import java.io.File;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import com.vaadin.data.Converter;
import com.vaadin.data.Result;
import com.vaadin.data.ValueContext;
import com.vaadin.icons.VaadinIcons;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.FileResource;
import com.vaadin.server.FontAwesome;
import com.vaadin.server.FontIcon;
import com.vaadin.server.GenericFontIcon;
import com.vaadin.server.Resource;
import com.vaadin.server.ResourceReference;
import com.vaadin.server.ThemeResource;
import com.vaadin.ui.declarative.DesignAttributeHandler;
/**
* A converter for {@link Resource} implementations supported by
* {@link DesignAttributeHandler}.
*
* @author Vaadin Ltd
* @since 7.4
*/
@SuppressWarnings("serial")
public class DesignResourceConverter implements Converter<String, Resource> {
private static final Map<Integer, VaadinIcons> CODE_POINTS =
Arrays.stream(VaadinIcons.values()).collect(Collectors.toMap(VaadinIcons::getCodepoint, icon -> icon));
@Override
public Result<Resource> convertToModel(String value, ValueContext context) {
if (!value.contains("://")) {
// assume it'is "file://" protocol, one that is used to access a
// file on a given path on the server, this will later be striped
// out anyway
value = "file://" + value;
}
String protocol = value.split("://")[0];
try {
ResourceConverterByProtocol converter = ResourceConverterByProtocol
.valueOf(protocol.toUpperCase(Locale.ENGLISH));
return Result.ok(converter.parse(value));
} catch (IllegalArgumentException iae) {
return Result.error("Unrecognized protocol: " + protocol);
}
}
@Override
public String convertToPresentation(Resource value, ValueContext context) {
ResourceConverterByProtocol byType = ResourceConverterByProtocol
.byType(value.getClass());
if (byType != null) {
return byType.format(value);
} else {
throw new IllegalArgumentException(
"unknown Resource type - " + value.getClass().getName());
}
}
private static interface ProtocolResourceConverter extends Serializable {
public String format(Resource value);
public Resource parse(String value);
}
private static enum ResourceConverterByProtocol
implements ProtocolResourceConverter {
HTTP, HTTPS, FTP, FTPS, THEME {
@Override
public Resource parse(String value) {
// strip "theme://" from the url, use the rest as the resource
// id
return new ThemeResource(value.substring(8));
}
@Override
public String format(Resource value) {
return new ResourceReference(value, null, null).getURL();
}
},
FONTICON {
@Override
public Resource parse(String value) {
final String address = value.split("://", 2)[1];
final String[] familyAndCode = address.split("/", 2);
final int codepoint = Integer.valueOf(familyAndCode[1], 16);
if (VAADIN_ICONS_NAME.equals(familyAndCode[0])) {
return CODE_POINTS.get(codepoint);
}
if (FontAwesome.FONT_FAMILY.equals(familyAndCode[0])) { //Left for compatibility
return FontAwesome.fromCodepoint(codepoint);
}
// all vaadin icons should have a codepoint
FontIcon generic = new GenericFontIcon(familyAndCode[0],
codepoint);
return generic;
}
@Override
public String format(Resource value) {
FontIcon icon = (FontIcon) value;
return new ResourceReference(icon, null, null).getURL();
}
},
@Deprecated
FONT {
@Override
public Resource parse(String value) {
// Deprecated, 7.4 syntax is
// font://"+FontAwesome.valueOf(foo) eg. "font://AMBULANCE"
final String iconName = value.split("://", 2)[1];
return FontAwesome.valueOf(iconName);
}
@Override
public String format(Resource value) {
throw new UnsupportedOperationException(
"Use " + ResourceConverterByProtocol.FONTICON.toString()
+ " instead");
}
},
FILE {
@Override
public Resource parse(String value) {
return new FileResource(new File(value.split("://")[1]));
}
@Override
public String format(Resource value) {
String path = ((FileResource) value).getSourceFile().getPath();
if (File.separatorChar != '/') {
// make sure we use '/' as file separator in templates
return path.replace(File.separatorChar, '/');
} else {
return path;
}
}
};
public static final String VAADIN_ICONS_NAME = VaadinIcons.ABACUS.getFontFamily();
@Override
public Resource parse(String value) {
// default behavior for HTTP, HTTPS, FTP and FTPS
return new ExternalResource(value);
}
@Override
public String format(Resource value) {
// default behavior for HTTP, HTTPS, FTP and FTPS
return ((ExternalResource) value).getURL();
}
private static final Map<Class<? extends Resource>, ResourceConverterByProtocol> typeToConverter = new HashMap<>();
static {
typeToConverter.put(ExternalResource.class, HTTP);
// ^ any of non-specialized would actually work
typeToConverter.put(ThemeResource.class, THEME);
typeToConverter.put(FontIcon.class, FONTICON);
typeToConverter.put(FileResource.class, FILE);
}
public static ResourceConverterByProtocol byType(
Class<? extends Resource> resourceType) {
for (Class<?> type : typeToConverter.keySet()) {
if (type.isAssignableFrom(resourceType)) {
return typeToConverter.get(type);
}
}
return null;
}
}
}