Annotate icon and tile classes with null annotations (#1490)

Signed-off-by: Wouter Born <github@maindrain.net>
This commit is contained in:
Wouter Born 2020-05-21 21:14:21 +02:00 committed by GitHub
parent 8808f04c30
commit 240c245b16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 117 additions and 104 deletions

View File

@ -12,19 +12,23 @@
*/
package org.openhab.core.io.rest.ui;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* This is an data transfer object for a UI tile.
*
* @author Yannick Schaus - Initial contribution
*/
@NonNullByDefault
public class TileDTO {
public String name;
public String url;
public String overlay;
public @Nullable String overlay;
public String imageUrl;
public TileDTO(String name, String url, String overlay, String imageUrl) {
public TileDTO(String name, String url, @Nullable String overlay, String imageUrl) {
super();
this.name = name;
this.url = url;

View File

@ -15,9 +15,10 @@ package org.openhab.core.ui.icon;
import java.io.InputStream;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.ui.icon.IconSet.Format;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -37,50 +38,30 @@ import org.slf4j.LoggerFactory;
*
* @author Kai Kreuzer - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractResourceIconProvider implements IconProvider {
private final Logger logger = LoggerFactory.getLogger(AbstractResourceIconProvider.class);
/**
* The OSGi bundle context
*/
protected BundleContext context;
protected final TranslationProvider i18nProvider;
/**
* An TranslationProvider service
*/
protected TranslationProvider i18nProvider;
/**
* When activating the service, we need to keep the bundle context.
*
* @param context the bundle context provided through OSGi DS.
*/
protected void activate(BundleContext context) {
this.context = context;
}
protected void setTranslationProvider(TranslationProvider i18nProvider) {
public AbstractResourceIconProvider(final TranslationProvider i18nProvider) {
this.i18nProvider = i18nProvider;
}
protected void unsetTranslationProvider(TranslationProvider i18nProvider) {
this.i18nProvider = null;
}
@Override
public Set<IconSet> getIconSets() {
return getIconSets(null);
}
@Override
public Integer hasIcon(String category, String iconSetId, Format format) {
public @Nullable Integer hasIcon(String category, String iconSetId, Format format) {
return hasResource(iconSetId, category.toLowerCase() + "." + format.toString().toLowerCase()) ? getPriority()
: null;
}
@Override
public InputStream getIcon(String category, String iconSetId, String state, Format format) {
public @Nullable InputStream getIcon(String category, String iconSetId, @Nullable String state, Format format) {
String resourceWithoutState = category.toLowerCase() + "." + format.toString().toLowerCase();
if (state == null) {
return getResource(iconSetId, resourceWithoutState);
@ -139,7 +120,7 @@ public abstract class AbstractResourceIconProvider implements IconProvider {
* @param resourceName the name of the resource
* @return the content as a stream or null, if the resource does not exist
*/
protected abstract InputStream getResource(String iconSetId, String resourceName);
protected abstract @Nullable InputStream getResource(String iconSetId, String resourceName);
/**
* Checks whether a certain resource exists for a given icon set.

View File

@ -16,6 +16,8 @@ import java.io.InputStream;
import java.util.Locale;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.ui.icon.IconSet.Format;
/**
@ -30,6 +32,7 @@ import org.openhab.core.ui.icon.IconSet.Format;
*
* @author Kai Kreuzer - Initial contribution
*/
@NonNullByDefault
public interface IconProvider {
/**
@ -45,7 +48,7 @@ public interface IconProvider {
* @param locale the locale to use for the results
* @return a set of icon sets in the requested locale
*/
Set<IconSet> getIconSets(Locale locale);
Set<IconSet> getIconSets(@Nullable Locale locale);
/**
* determines whether this provider can deliver an icon for a given name
@ -57,6 +60,7 @@ public interface IconProvider {
* this provider cannot deliver an icon. Default for full icon sets should be 0, so that others have the
* chance to override icons.
*/
@Nullable
Integer hasIcon(String category, String iconSetId, Format format);
/**
@ -68,5 +72,6 @@ public interface IconProvider {
* @param format the format of the stream (usually either png or svg)
* @return a byte stream of the icon in the given format or null, if no icon exists
*/
InputStream getIcon(String category, String iconSetId, String state, Format format);
@Nullable
InputStream getIcon(String category, String iconSetId, @Nullable String state, Format format);
}

View File

@ -16,11 +16,14 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* This is a bean that provides some meta-information about available icon sets.
*
* @author Kai Kreuzer - Initial contribution
*/
@NonNullByDefault
public class IconSet {
/**
@ -31,10 +34,10 @@ public class IconSet {
SVG
}
private String id;
private String label;
private String description;
private Set<Format> formats;
private final String id;
private final String label;
private final String description;
private final Set<Format> formats;
/**
* Construct a new pojo.

View File

@ -20,11 +20,16 @@ import java.util.Collections;
import java.util.Locale;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.config.core.ConfigConstants;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.ui.icon.AbstractResourceIconProvider;
import org.openhab.core.ui.icon.IconProvider;
import org.openhab.core.ui.icon.IconSet;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The custom icon provider supports custom icons in the configurations/icons
@ -33,26 +38,27 @@ import org.osgi.service.component.annotations.Component;
* @author Kai Kreuzer - Initial contribution
*/
@Component(immediate = true, service = { IconProvider.class })
@NonNullByDefault
public class CustomIconProvider extends AbstractResourceIconProvider {
private File getIconFile(String filename, String iconSetId) {
@Activate
public CustomIconProvider(final @Reference TranslationProvider i18nProvider) {
super(i18nProvider);
}
private @Nullable File getIconFile(String filename, String iconSetId) {
File folder = new File(
ConfigConstants.getConfigFolder() + File.separator + "icons" + File.separator + iconSetId);
File file = new File(folder, filename);
if (file.exists()) {
return file;
} else {
return null;
}
return file.exists() ? file : null;
}
@Override
protected InputStream getResource(String iconSetId, String resourceName) {
protected @Nullable InputStream getResource(String iconSetId, String resourceName) {
File file = getIconFile(resourceName, iconSetId);
if (file != null) {
try {
FileInputStream is = new FileInputStream(file);
return is;
return new FileInputStream(file);
} catch (FileNotFoundException e) {
return null;
}
@ -62,12 +68,11 @@ public class CustomIconProvider extends AbstractResourceIconProvider {
@Override
protected boolean hasResource(String iconSetId, String resourceName) {
File file = getIconFile(resourceName, iconSetId);
return file != null;
return getIconFile(resourceName, iconSetId) != null;
}
@Override
public Set<IconSet> getIconSets(Locale locale) {
public Set<IconSet> getIconSets(@Nullable Locale locale) {
return Collections.emptySet();
}

View File

@ -24,6 +24,8 @@ import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.io.http.servlet.SmartHomeServlet;
import org.openhab.core.ui.icon.IconProvider;
import org.openhab.core.ui.icon.IconSet.Format;
@ -45,6 +47,7 @@ import org.slf4j.LoggerFactory;
* @author Kai Kreuzer - Initial contribution
*/
@Component
@NonNullByDefault
public class IconServlet extends SmartHomeServlet {
private static final long serialVersionUID = 2880642275858634578L;
@ -61,7 +64,7 @@ public class IconServlet extends SmartHomeServlet {
protected String defaultIconSetId = "classic";
private List<IconProvider> iconProvider = new ArrayList<>();
private final List<IconProvider> iconProvider = new ArrayList<>();
@Activate
public IconServlet(final @Reference HttpService httpService, final @Reference HttpContext httpContext) {
@ -99,38 +102,44 @@ public class IconServlet extends SmartHomeServlet {
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
protected void doGet(@NonNullByDefault({}) HttpServletRequest req, @NonNullByDefault({}) HttpServletResponse resp)
throws ServletException, IOException {
if (req.getDateHeader("If-Modified-Since") > startupTime) {
resp.setStatus(304);
return;
}
String category = getCategory(req);
Format format = getFormat(req);
String state = getState(req);
String iconSetId = getIconSetId(req);
Format format = getFormat(req);
Format otherFormat = null;
if ("true".equalsIgnoreCase(req.getParameter(PARAM_ANY_FORMAT))) {
otherFormat = (format == Format.PNG) ? Format.SVG : Format.PNG;
}
IconProvider provider = getIconProvider(category, iconSetId, format);
IconProvider provider2 = getIconProvider(category, iconSetId, otherFormat);
if (provider2 != null) {
if (provider == null) {
provider = provider2;
format = otherFormat;
} else if (provider2 != provider) {
Integer prio = provider.hasIcon(category, iconSetId, format);
Integer prio2 = provider2.hasIcon(category, iconSetId, otherFormat);
if (prio != null && prio2 != null && prio < prio2) {
if (otherFormat != null) {
IconProvider provider2 = getIconProvider(category, iconSetId, otherFormat);
if (provider2 != null) {
if (provider == null) {
provider = provider2;
format = otherFormat;
} else if (provider2 != provider) {
Integer prio = provider.hasIcon(category, iconSetId, format);
Integer prio2 = provider2.hasIcon(category, iconSetId, otherFormat);
if ((prio != null && prio2 != null && prio < prio2) || (prio == null && prio2 != null)) {
provider = provider2;
format = otherFormat;
}
}
}
}
if (provider == null) {
logger.debug("Requested icon category {} provided by no icon provider;", category);
logger.debug("Requested icon category {} provided by no icon provider", category);
resp.sendError(404);
return;
}
@ -143,6 +152,11 @@ public class IconServlet extends SmartHomeServlet {
resp.setDateHeader("Last-Modified", new Date().getTime());
ServletOutputStream os = resp.getOutputStream();
try (InputStream is = provider.getIcon(category, iconSetId, state, format)) {
if (is == null) {
logger.debug("Requested icon category {} provided by no icon provider", category);
resp.sendError(404);
return;
}
is.transferTo(os);
resp.flushBuffer();
} catch (IOException e) {
@ -192,7 +206,7 @@ public class IconServlet extends SmartHomeServlet {
}
}
private String getState(HttpServletRequest req) {
private @Nullable String getState(HttpServletRequest req) {
String state = req.getParameter(PARAM_STATE);
if (state != null) {
return state;
@ -208,16 +222,14 @@ public class IconServlet extends SmartHomeServlet {
}
}
private IconProvider getIconProvider(String category, String iconSetId, Format format) {
private @Nullable IconProvider getIconProvider(String category, String iconSetId, Format format) {
IconProvider topProvider = null;
if (format != null) {
int maxPrio = Integer.MIN_VALUE;
for (IconProvider provider : iconProvider) {
Integer prio = provider.hasIcon(category, iconSetId, format);
if (prio != null && prio > maxPrio) {
maxPrio = prio;
topProvider = provider;
}
int maxPrio = Integer.MIN_VALUE;
for (IconProvider provider : iconProvider) {
Integer prio = provider.hasIcon(category, iconSetId, format);
if (prio != null && prio > maxPrio) {
maxPrio = prio;
topProvider = provider;
}
}
return topProvider;

View File

@ -14,6 +14,7 @@ package org.openhab.core.ui.icon;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import static org.mockito.MockitoAnnotations.initMocks;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -25,6 +26,8 @@ import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.ui.icon.IconSet.Format;
/**
@ -37,9 +40,13 @@ public class AbstractResourceIconProviderTest {
private IconProvider provider;
private @Mock TranslationProvider i18nProviderMock;
@Before
public void setUp() {
provider = new AbstractResourceIconProvider() {
initMocks(this);
provider = new AbstractResourceIconProvider(i18nProviderMock) {
@Override
protected InputStream getResource(String iconset, String resourceName) {
switch (resourceName) {

View File

@ -17,14 +17,12 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.ui.tiles.ExternalServiceTile;
import org.openhab.core.ui.tiles.Tile;
import org.openhab.core.ui.tiles.TileProvider;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
@ -40,36 +38,22 @@ import org.slf4j.LoggerFactory;
* @author Yannick Schaus - refactor into tile service, remove dashboard components
*/
@Component(immediate = true, name = "org.openhab.core.ui.tiles")
@NonNullByDefault
public class TileService implements TileProvider {
private final Logger logger = LoggerFactory.getLogger(TileService.class);
protected ConfigurationAdmin configurationAdmin;
protected Set<Tile> tiles = new CopyOnWriteArraySet<>();
private static final String LINK_NAME = "link-name";
private static final String LINK_URL = "link-url";
private static final String LINK_IMAGEURL = "link-imageurl";
private final Logger logger = LoggerFactory.getLogger(TileService.class);
private final Set<Tile> tiles = new CopyOnWriteArraySet<>();
@Activate
protected void activate(ComponentContext componentContext, Map<String, Object> properties) {
public TileService(Map<String, Object> properties) {
addTilesForExternalServices(properties);
}
@Deactivate
protected void deactivate(ComponentContext componentContext) {
}
@Reference
protected void setConfigurationAdmin(ConfigurationAdmin configurationAdmin) {
this.configurationAdmin = configurationAdmin;
}
protected void unsetConfigurationAdmin(ConfigurationAdmin configurationAdmin) {
this.configurationAdmin = null;
}
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
protected void addTile(Tile tile) {
tiles.add(tile);
@ -98,7 +82,7 @@ public class TileService implements TileProvider {
Tile newTile = new ExternalServiceTile.TileBuilder().withName(name).withUrl(url)
.withImageUrl(imageUrl).build();
if (name != null && url != null && !name.isEmpty() && !url.isEmpty()) {
if (!name.isEmpty() && !url.isEmpty()) {
addTile(newTile);
logger.debug("Tile added: {}", newTile);
} else {

View File

@ -12,16 +12,20 @@
*/
package org.openhab.core.ui.tiles;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The dashboard tile for external services.
*
* @author Pauli Anttila - Initial contribution
* @author Yannick Schaus - moved into core, remove references to dashboard
*/
@NonNullByDefault
public class ExternalServiceTile implements Tile {
private String name;
private String url;
private String overlay;
private @Nullable String overlay;
private String imageUrl;
private ExternalServiceTile(TileBuilder builder) {
@ -42,7 +46,7 @@ public class ExternalServiceTile implements Tile {
}
@Override
public String getOverlay() {
public @Nullable String getOverlay() {
return overlay;
}
@ -56,7 +60,7 @@ public class ExternalServiceTile implements Tile {
final int maxlen = 100;
String limitedImageUrl = imageUrl;
if (limitedImageUrl != null && limitedImageUrl.length() > maxlen) {
if (limitedImageUrl.length() > maxlen) {
limitedImageUrl = imageUrl.substring(0, maxlen) + "...";
}
@ -65,10 +69,10 @@ public class ExternalServiceTile implements Tile {
public static class TileBuilder {
private String name;
private String url;
private String overlay;
private String imageUrl;
private String name = "";
private String url = "";
private @Nullable String overlay;
private String imageUrl = "";
public TileBuilder withName(String name) {
this.name = name;
@ -80,7 +84,7 @@ public class ExternalServiceTile implements Tile {
return this;
}
public TileBuilder withOverlay(String overlay) {
public TileBuilder withOverlay(@Nullable String overlay) {
this.overlay = overlay;
return this;
}

View File

@ -12,6 +12,9 @@
*/
package org.openhab.core.ui.tiles;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* A tile can be registered by an UI as a service in order to appear on the main openHAB UI.
*
@ -19,6 +22,7 @@ package org.openhab.core.ui.tiles;
* @author Yannick Schaus - refactored into core, remove references to dashboard
*
*/
@NonNullByDefault
public interface Tile {
/**
@ -48,5 +52,6 @@ public interface Tile {
*
* @return the overlay to use
*/
@Nullable
String getOverlay();
}

View File

@ -14,11 +14,14 @@ package org.openhab.core.ui.tiles;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Interface for a component providing UI tiles.
*
* @author Yannick Schaus - initial contribution
* @author Yannick Schaus - Initial contribution
*/
@NonNullByDefault
public interface TileProvider {
public Stream<Tile> getTiles();
}