Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

added some null annotations to core APIs #3933

Merged
merged 1 commit into from
Aug 4, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Bundle-Version: 0.9.0.qualifier
Bundle-Vendor: Eclipse.org/SmartHome
Import-Package:
com.google.gson,
org.eclipse.jdt.annotation;resolution:=optional,
org.eclipse.smarthome.automation,
org.eclipse.smarthome.automation.core.util,
org.eclipse.smarthome.automation.events,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.smarthome.automation.Action;
import org.eclipse.smarthome.automation.Condition;
import org.eclipse.smarthome.automation.Module;
Expand Down Expand Up @@ -1087,7 +1087,7 @@ protected synchronized RuleStatusInfo getRuleStatusInfo(String rUID) {
return statusMap.get(rUID);
}

protected synchronized String getUniqueId() {
protected synchronized @NonNull String getUniqueId() {
int result = 0;
if (rules != null) {
Set<String> col = rules.keySet();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.Set;
import java.util.stream.Stream;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.smarthome.automation.Rule;
import org.eclipse.smarthome.automation.RuleProvider;
import org.eclipse.smarthome.automation.RuleRegistry;
Expand Down Expand Up @@ -230,17 +231,19 @@ protected void removeModuleHandlerFactory(ModuleHandlerFactory moduleHandlerFact
*/
@Override
public Rule add(Rule rule) {
if (rule == null) {
throw new IllegalArgumentException("The added rule must not be null!");
}
String rUID = rule.getUID();
if (rUID == null) {
rUID = ruleEngine.getUniqueId();
super.add(initRuleId(rUID, rule));
} else {
super.add(rule);
}
return get(rUID);
Rule ruleCopy = get(rUID);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible the AbstractRegistry to return a null value for already added element? If so it is a bug in the AbstractRegistry implementation for me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it is a bug in your train of thoughts - which proves that the annotations are helpful :-)
We are requesting a rule of a certain UID here. You cannot assume that it exists here. Although you have added it previously, somebody else might have deleted it again or the initRuleId method might have decided to store it under a different UID - how could you tell?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initRuleId won't change id because it is my method, but you are right that someone can meanwhile remove the added element. Ok this approach is better then synchronization for me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because it is my method

There is no such thing as "my method" in an open source project. Make sure to implement robust code that does not do too many implicit assumptions that only exist in your mind.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"my method"

I mean that this is an implementation detail which is not exposed by the API. We have to find a limit where to stop with annotations. Do we have to set annotation on the private methods too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If they are helpful as they remove a warning at another place as in this case, what speaks against it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a worning in places where I use this method. Btw these warnings are related with missing null checks. Isn't be better to have handle these situations (as we saw that @nonnull does not defends us) and provider a suitable exception instead of just a NullPointerException at runtime.
Anyway I can live with this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we saw that @nonnull does not defends us

It actually defends us especially on internal methods, because we have checks in place in our IDE and the Maven build. So we can be sure that all ESH code itself respects the annotations - imho definitely helpful for any kind of future refactorings.

if (ruleCopy != null) {
return ruleCopy;
} else {
throw new IllegalStateException();
}
}

/**
Expand All @@ -251,7 +254,7 @@ public Rule add(Rule rule) {
* @param rule candidate for unique ID
* @return a rule with UID
*/
protected Rule initRuleId(String rUID, Rule rule) {
protected @NonNull Rule initRuleId(String rUID, Rule rule) {
Rule ruleWithUID = new Rule(rUID, rule.getTriggers(), rule.getConditions(), rule.getActions(),
rule.getConfigurationDescriptions(), rule.getConfiguration(), rule.getTemplateUID(),
rule.getVisibility());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Bundle-SymbolicName: org.eclipse.smarthome.automation.module.script.rulesupport
Bundle-Version: 0.9.0.qualifier
Bundle-Vendor: Eclipse.org/SmartHome
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: org.eclipse.smarthome.automation,
Import-Package: org.eclipse.jdt.annotation;resolution:=optional,
org.eclipse.smarthome.automation,
org.eclipse.smarthome.automation.handler,
org.eclipse.smarthome.automation.module.script,
org.eclipse.smarthome.automation.module.script.rulesupport.shared,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Bundle-Vendor: Eclipse.org/SmartHome
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.google.common.base,
com.google.common.collect,
org.eclipse.jdt.annotation;resolution:=optional,
org.eclipse.smarthome.config.core,
org.eclipse.smarthome.config.discovery,
org.eclipse.smarthome.config.discovery.dto,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ Bundle-Version: 0.9.0.qualifier
Bundle-ManifestVersion: 2
Bundle-License: http://www.eclipse.org/legal/epl-v10.html
Bundle-SymbolicName: org.eclipse.smarthome.core.persistence
Import-Package: org.eclipse.smarthome.config.core,
Import-Package: org.eclipse.jdt.annotation;resolution:=optional,
org.eclipse.smarthome.config.core,
org.eclipse.smarthome.core.common.registry,
org.eclipse.smarthome.core.items,
org.eclipse.smarthome.core.persistence,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Import-Package: com.google.common.base,
org.apache.commons.collections.iterators,
org.apache.commons.io,
org.apache.commons.lang,
org.eclipse.jdt.annotation;resolution:=optional,
org.eclipse.smarthome.config.core,
org.eclipse.smarthome.config.core.dto,
org.eclipse.smarthome.config.core.status,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Import-Package: com.google.common.base,
com.google.gson,
org.apache.commons.io,
org.apache.commons.lang,
org.eclipse.jdt.annotation;resolution:=optional,
org.eclipse.smarthome.core.auth,
org.eclipse.smarthome.core.binding,
org.eclipse.smarthome.core.binding.dto,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import java.util.Collection;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.smarthome.core.storage.Storage;
import org.eclipse.smarthome.core.storage.StorageService;
import org.slf4j.Logger;
Expand Down Expand Up @@ -46,11 +47,6 @@ public abstract class AbstractManagedProvider<E extends Identifiable<K>, K, PE>

@Override
public void add(E element) {

if (element == null) {
throw new IllegalArgumentException("Cannot add null element");
}

String keyAsString = getKeyAsString(element);
if (storage.get(keyAsString) != null) {
throw new IllegalArgumentException(
Expand All @@ -67,9 +63,13 @@ public Collection<E> getAll() {
final Function<String, E> toElementList = new Function<String, E>() {
@Override
public E apply(String elementKey) {
PE persistableElement = storage.get(elementKey);
if (persistableElement != null) {
return toElement(elementKey, persistableElement);
if (elementKey != null) {
PE persistableElement = storage.get(elementKey);
if (persistableElement != null) {
return toElement(elementKey, persistableElement);
} else {
return null;
}
} else {
return null;
}
Expand Down Expand Up @@ -101,11 +101,6 @@ public E get(K key) {

@Override
public E remove(K key) {

if (key == null) {
throw new IllegalArgumentException("Cannot remove null element");
}

String keyAsString = keyToString(key);
PE persistableElement = storage.remove(keyAsString);
if (persistableElement != null) {
Expand All @@ -122,11 +117,6 @@ public E remove(K key) {

@Override
public E update(E element) {

if (element == null) {
throw new IllegalArgumentException("Cannot update null element");
}

String key = getKeyAsString(element);
if (storage.get(key) != null) {
PE persistableElement = storage.put(key, toPersistableElement(element));
Expand All @@ -142,7 +132,7 @@ public E update(E element) {
return null;
}

private String getKeyAsString(E element) {
private @NonNull String getKeyAsString(@NonNull E element) {
return keyToString(element.getUID());
}

Expand All @@ -160,7 +150,7 @@ private String getKeyAsString(E element) {
* key
* @return string representation of the key
*/
protected abstract String keyToString(K key);
protected abstract @NonNull String keyToString(@NonNull K key);

protected void setStorageService(StorageService storageService) {
this.storage = storageService.getStorage(getStorageName(), this.getClass().getClassLoader());
Expand All @@ -174,7 +164,7 @@ protected void setStorageService(StorageService storageService) {
* persistable element
* @return original element
*/
protected abstract E toElement(String key, PE persistableElement);
protected abstract E toElement(@NonNull String key, @NonNull PE persistableElement);

/**
* Converts the original element into an element that can be persisted.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.smarthome.core.events.Event;
import org.eclipse.smarthome.core.events.EventPublisher;
import org.osgi.framework.BundleContext;
Expand Down Expand Up @@ -139,8 +140,9 @@ public void addRegistryChangeListener(RegistryChangeListener<E> listener) {
listeners.add(listener);
}

@SuppressWarnings("null")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to check this one (some time)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be happy if you'd find a better solution - my issue is that the whole Java8 Stream API is pretty much incompatible with those annotations. Which might be a hint that for all new stream based APIs that we introduce, we might indeed want to consider the use of Optional.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See below...

@Override
public Collection<E> getAll() {
public Collection<@NonNull E> getAll() {
return stream().collect(Collectors.toList());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
*/
package org.eclipse.smarthome.core.common.registry;

import org.eclipse.jdt.annotation.NonNull;

/**
* The {@link ManagedProvider} is a specific {@link Provider} that enables to
* add, remove and update elements at runtime.
Expand All @@ -26,7 +28,7 @@ public interface ManagedProvider<E extends Identifiable<K>, K> extends Provider<
* @param element
* element to be added
*/
void add(E element);
void add(@NonNull E element);

/**
* Removes an element and returns the removed element.
Expand All @@ -36,7 +38,7 @@ public interface ManagedProvider<E extends Identifiable<K>, K> extends Provider<
* @return element that was removed, or null if no element with the given
* key exists
*/
E remove(K key);
E remove(@NonNull K key);

/**
* Updates an element.
Expand All @@ -46,7 +48,7 @@ public interface ManagedProvider<E extends Identifiable<K>, K> extends Provider<
* @return returns the old element or null if no element with the same key
* exists
*/
E update(E element);
E update(@NonNull E element);

/**
* Returns an element for the given key or null if no element for the given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@
import java.util.Collection;
import java.util.stream.Stream;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;

/**
* The {@link Registry} interface represents a registry for elements of the type
* E. The concrete sub interfaces are registered as OSGi services.
*
* @author Dennis Nobel - Initial contribution
* @author Victor Toni - provide elements as {@link Stream}
* @author Kai Kreuzer - added null annotations
*
* @param <E> type of the elements in the registry
*/
Expand All @@ -26,14 +30,15 @@ public interface Registry<E extends Identifiable<K>, K> {
*
* @param listener registry change listener
*/
void addRegistryChangeListener(RegistryChangeListener<E> listener);
void addRegistryChangeListener(@NonNull RegistryChangeListener<E> listener);

/**
* Returns a collection of all elements in the registry.
*
* @return collection of all elements in the registry
*/
Collection<E> getAll();
@NonNull
Collection<@NonNull E> getAll();

/**
* Returns a stream of all elements in the registry.
Expand All @@ -49,15 +54,15 @@ public interface Registry<E extends Identifiable<K>, K> {
* key of the element
* @return element or null if no element was found
*/
public E get(K key);
public @Nullable E get(K key);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see what information gives this annotation here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It says that the return value might be null (and by adding it, we state that this is deliberate and not that we merely didn't bother adding a @Nonnull annotation).

Copy link
Contributor

@danchom danchom Aug 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concerns was that exactly this was said in the javadoc (but in case if it is not read ok) and it is obvious for me that it will return a null for any key without value (as a Map).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is obvious for me that it will return a null for any key without value

Well, there are also other examples, so making it explicit is a good thing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand right the ItemRegistry has a duplicate get method named getItem() which throws and exception instead of returning null. Isn't be better to set @nonnull to ItemRegistry.getItem() instead of @nullable to all registries?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can change the return value from @Nullable of a parent class to @Nonnull in a child but you can't change from @Nonnull to @Nullable.

It is the same logic used for more specialized returned classes used by the JVM.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't be better to set @nonnull to ItemRegistry.getItem() instead of @nullable to all registries?

getItem() obviously only exists on ItemRegistry, so I do not understand the question.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maggu2810 these are two different methods and the getItem() does not overwrite the get(), My proposal was: Registry.get() to be not annotated, because it is obvious that it returns null for missing elements (as any Map), and to annotated @nonnull ItemRegistry.getItem() because it never return a null.
But if you think current approach is better I'm ok with it.


/**
* Removes a {@link RegistryChangeListener} from the registry.
*
* @param listener
* registry change listener
*/
void removeRegistryChangeListener(RegistryChangeListener<E> listener);
void removeRegistryChangeListener(@NonNull RegistryChangeListener<E> listener);

/**
* Adds the given element to the according {@link ManagedProvider}.
Expand All @@ -68,7 +73,7 @@ public interface Registry<E extends Identifiable<K>, K> {
* @throws IllegalStateException
* if no ManagedProvider is available
*/
public E add(E element);
public @NonNull E add(@NonNull E element);

/**
* Updates the given element at the according {@link ManagedProvider}.
Expand All @@ -80,7 +85,7 @@ public interface Registry<E extends Identifiable<K>, K> {
* @throws IllegalStateException
* if no ManagedProvider is available
*/
public E update(E element);
public @Nullable E update(@NonNull E element);

/**
* Removes the given element from the according {@link ManagedProvider}.
Expand All @@ -92,5 +97,5 @@ public interface Registry<E extends Identifiable<K>, K> {
* @throws IllegalStateException
* if no ManagedProvider is available
*/
public E remove(K key);
public @Nullable E remove(@NonNull K key);
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,13 @@ public Item getItemByPattern(String name) throws ItemNotFoundException, ItemNotU
throw new ItemNotUniqueException(name, items);
}

return items.iterator().next();
Item item = items.iterator().next();

if (item == null) {
throw new ItemNotFoundException(name);
} else {
return item;
}
}

@Override
Expand Down Expand Up @@ -107,13 +112,15 @@ public Collection<Item> getItems(String pattern) {

private void addToGroupItems(Item item, List<String> groupItemNames) {
for (String groupName : groupItemNames) {
try {
Item groupItem = getItem(groupName);
if (groupItem instanceof GroupItem) {
((GroupItem) groupItem).addMember(item);
if (groupName != null) {
try {
Item groupItem = getItem(groupName);
if (groupItem instanceof GroupItem) {
((GroupItem) groupItem).addMember(item);
}
} catch (ItemNotFoundException e) {
// the group might not yet be registered, let's ignore this
}
} catch (ItemNotFoundException e) {
// the group might not yet be registered, let's ignore this
}
}
}
Expand Down Expand Up @@ -166,13 +173,15 @@ private void addMembersToGroupItem(GroupItem groupItem) {

private void removeFromGroupItems(Item item, List<String> groupItemNames) {
for (String groupName : groupItemNames) {
try {
Item groupItem = getItem(groupName);
if (groupItem instanceof GroupItem) {
((GroupItem) groupItem).removeMember(item);
if (groupName != null) {
try {
Item groupItem = getItem(groupName);
if (groupItem instanceof GroupItem) {
((GroupItem) groupItem).removeMember(item);
}
} catch (ItemNotFoundException e) {
// the group might not yet be registered, let's ignore this
}
} catch (ItemNotFoundException e) {
// the group might not yet be registered, let's ignore this
}
}
}
Expand Down
Loading