Skip to content

Commit

Permalink
added attribute
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Persson <daniel.p.persson@husqvarnagroup.com>
  • Loading branch information
danielhqv committed Mar 9, 2018
1 parent af9e031 commit 81df3e0
Show file tree
Hide file tree
Showing 7 changed files with 544 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*******************************************************************************
* Copyright (c) 2013-2018 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial API and implementation
* Daniel Persson (Husqvarna Group) - Attribute support
*******************************************************************************/
package org.eclipse.leshan.core.attributes;

public enum AccessMode {
R,
W,
RW
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2013-2018 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial API and implementation
* Daniel Persson (Husqvarna Group) - Attribute support
*******************************************************************************/
package org.eclipse.leshan.core.attributes;

/**
* The assignation level of an {@link Attribute}. An attribute can only be applied on one level,
* but it can be assigned on many levels and then be inherited down to its application
* level.
*/
public enum AssignationLevel {
OBJECT,
INSTANCE,
RESOURCE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2013-2018 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial API and implementation
* Daniel Persson (Husqvarna Group) - Attribute support
*******************************************************************************/
package org.eclipse.leshan.core.attributes;

/**
* The attachment level of an LwM2m attribute.
*
* This indicates the level (object, instance or resource) where an attribute can
* be applied. E.g. the 'pmin' attribute can only be applied on the Resource level,
* but it can be assigned on all levels. 'pmin' attributes that are assigned to
* the object or instance level are then inherited by all resources that don't have
* their own 'pmin' attribute.
*/
public enum Attachment {
OBJECT,
INSTANCE,
RESOURCE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*******************************************************************************
* Copyright (c) 2013-2018 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial API and implementation
* Daniel Persson (Husqvarna Group) - Attribute support
*******************************************************************************/
package org.eclipse.leshan.core.attributes;

import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.leshan.util.Validate;

import java.util.Set;

/**
* Represents an LwM2m attribute that can be attached to an object, instance or resource.
*
* The {@link Attachment} level of the attribute indicates where it can be applied, e.g.
* the 'pmin' attribute is only applicable to resources, but it can be assigned on all levels
* and then inherited by underlying resources.
*/
public class Attribute {

public static final String DIMENSION = "dim";
public static final String OBJECT_VERSION = "ver";
public static final String MINIMUM_PERIOD = "pmin";
public static final String MAXIMUM_PERIOD = "pmax";
public static final String GREATER_THAN = "gt";
public static final String LESSER_THAN = "lt";
public static final String STEP = "st";

/**
* Metadata container for LwM2m attributes
*/
private static class AttributeModel {
private final String coRELinkParam;
private final Attachment attachment;
private final Set<AssignationLevel> assignationLevels;
private final AccessMode accessMode;
private final Class<?> valueClass;

private AttributeModel(String coRELinkParam, Attachment attachment, Set<AssignationLevel> assignationLevels,
AccessMode accessMode, Class<?> valueClass) {
this.coRELinkParam = coRELinkParam;
this.attachment = attachment;
this.assignationLevels = assignationLevels;
this.accessMode = accessMode;
this.valueClass = valueClass;
}
}

private static Map<String, AttributeModel> modelMap;

static {
modelMap = new HashMap<>();
modelMap.put(DIMENSION, new Attribute.AttributeModel(DIMENSION, Attachment.RESOURCE, EnumSet.of(AssignationLevel.RESOURCE),
AccessMode.R, Long.class));
modelMap.put(OBJECT_VERSION, new Attribute.AttributeModel(OBJECT_VERSION, Attachment.OBJECT, EnumSet.of(AssignationLevel.OBJECT),
AccessMode.R, String.class));
modelMap.put(MINIMUM_PERIOD, new Attribute.AttributeModel(MINIMUM_PERIOD, Attachment.RESOURCE,
EnumSet.of(AssignationLevel.OBJECT, AssignationLevel.INSTANCE, AssignationLevel.RESOURCE),
AccessMode.RW, Long.class));
modelMap.put(MAXIMUM_PERIOD, new Attribute.AttributeModel(MAXIMUM_PERIOD, Attachment.RESOURCE,
EnumSet.of(AssignationLevel.OBJECT, AssignationLevel.INSTANCE, AssignationLevel.RESOURCE),
AccessMode.RW, Long.class));
modelMap.put(GREATER_THAN, new AttributeModel(GREATER_THAN, Attachment.RESOURCE,
EnumSet.of(AssignationLevel.RESOURCE), AccessMode.RW, Double.class));
modelMap.put(LESSER_THAN, new AttributeModel(LESSER_THAN, Attachment.RESOURCE,
EnumSet.of(AssignationLevel.RESOURCE), AccessMode.RW, Double.class));
modelMap.put(STEP, new AttributeModel(STEP, Attachment.RESOURCE,
EnumSet.of(AssignationLevel.RESOURCE), AccessMode.RW, Double.class));
}

private final AttributeModel model;
private final Object value;

public Attribute(String coRELinkParam, Object value) {
Validate.notEmpty(coRELinkParam);
this.model = modelMap.get(coRELinkParam);
if (model == null) {
throw new IllegalArgumentException(String.format("Unsupported attribute '%s'", coRELinkParam));
}
this.value = ensureMatchingValue(model, value);
}

public static Attribute create(String coRELinkParam, Object value) {
return new Attribute(coRELinkParam, value);
}

/**
* Ensures that a provided attribute value matches the attribute value type, including trying
* to perform a correct conversion if the value is a string, e.g.
* @return the converted or original value
*/
private Object ensureMatchingValue(AttributeModel model, Object value) {
// Ensure that the attribute value has the correct type
// If the value is a string, we make an attempt to convert it
Class<?> expectedClass = model.valueClass;
if (!expectedClass.equals(value.getClass()) && value instanceof String) {
if (expectedClass.equals(Long.class)) {
return Long.parseLong(value.toString());
} else if (expectedClass.equals(Double.class)) {
return Double.parseDouble(value.toString());
}
} else if (!this.model.valueClass.equals(value.getClass())) {
throw new IllegalArgumentException(String.format("Attribute '%s' must have a value of type %s",
model.coRELinkParam, model.valueClass.getSimpleName()));
}
return value;
}

public String getCoRELinkParam() {
return model.coRELinkParam;
}

public Object getValue() {
return value;
}

public Attachment getAttachment() {
return model.attachment;
}

public boolean isWritable() {
return model.accessMode == AccessMode.W || model.accessMode == AccessMode.RW;
}

public boolean canBeAssignedTo(AssignationLevel assignationLevel) {
return model.assignationLevels.contains(assignationLevel);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*******************************************************************************
* Copyright (c) 2013-2018 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Sierra Wireless - initial API and implementation
* Daniel Persson (Husqvarna Group) - Attribute support
*******************************************************************************/
package org.eclipse.leshan.core.attributes;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.eclipse.leshan.util.Validate;

/**
* A collection of {@link Attribute} instances that are handled as a collection
* that must adhere to rules that are specified in LwM2m, e.g. that the 'pmin' attribute
* must be less than the 'pmax' attribute, if they're both part of the same AttributeSet.
*/
public class AttributeSet {

private final Map<String, Attribute> attributeMap = new LinkedHashMap<>();

public AttributeSet(Attribute...attributes) {
this(Arrays.asList(attributes));
}

public AttributeSet(Collection<Attribute> attributes) {
if (attributes != null && !attributes.isEmpty()) {
for (Attribute attr : attributes) {
// Check for duplicates
if (attributeMap.containsKey(attr.getCoRELinkParam())) {
throw new IllegalArgumentException(String.format("Cannot create attribute set with duplicates (attr: '%s')",
attr.getCoRELinkParam()));
}
attributeMap.put(attr.getCoRELinkParam(), attr);
}
}
}

/**
* Creates an attribute set from a list of query params.
*/
@Deprecated
public AttributeSet(String[] queryParams) {
for (String param : queryParams) {
String[] keyAndValue = param.split("=");
if (keyAndValue.length != 2) {
throw new IllegalArgumentException(String.format("Cannot parse query param '%s'", param));
}
Attribute attr = Attribute.create(keyAndValue[0], keyAndValue[1]);
attributeMap.put(attr.getCoRELinkParam(), attr);
}
}

public void validate(AssignationLevel assignationLevel) {
// Can all attributes be assigned to this level?
for (Attribute attr : attributeMap.values()) {
if (!attr.canBeAssignedTo(assignationLevel)) {
throw new IllegalArgumentException(String.format("Attribute '%s' cannot be assigned to level %s",
attr.getCoRELinkParam(), assignationLevel.name()));
}
}
Attribute pmin = attributeMap.get(Attribute.MINIMUM_PERIOD);
Attribute pmax = attributeMap.get(Attribute.MAXIMUM_PERIOD);
if ((pmin != null) && (pmax != null) && (Long) pmin.getValue() > (Long) pmax.getValue()) {
throw new IllegalArgumentException(String.format("Cannot write attributes where '%s' > '%s'",
pmin.getCoRELinkParam(), pmax.getCoRELinkParam()));
}
}


/**
* Returns a new AttributeSet, containing only the attributes that have a matching
* Attachment level.
* @param attachment the Attachment level to filter by
* @return a new {@link AttributeSet} containing the filtered attributes
*/
public AttributeSet filter(Attachment attachment) {
List<Attribute> attrs = new ArrayList<>();
for (Attribute attr : getAttributes()) {
if (attr.getAttachment() == attachment) {
attrs.add(attr);
}
}
return new AttributeSet(attrs);
}

/**
* Creates a new AttributeSet by merging another AttributeSet onto this instance.
* @param attributes the AttributeSet that should be merged onto this instance. Attributes in this
* set will overwrite existing attribute values, if present. If this is null, the
* new attribute set will effectively be a clone of the existing one
* @return the merged AttributeSet
*/
public AttributeSet merge(AttributeSet attributes) {
Map<String, Attribute> merged = new LinkedHashMap<>();
for (Attribute attr : getAttributes()) {
merged.put(attr.getCoRELinkParam(), attr);
}
if (attributes != null) {
for (Attribute attr : attributes.getAttributes()) {
merged.put(attr.getCoRELinkParam(), attr);
}
}
return new AttributeSet(merged.values());
}

/**
* Returns the attributes as a map with the CoRELinkParam as key and the attribute value as map value.
* @return the attributes map
*/
public Map<String, Object> getMap() {
Map<String, Object> result = new LinkedHashMap<>();
for (Attribute attr : attributeMap.values()) {
result.put(attr.getCoRELinkParam(), attr.getValue());
}
return Collections.unmodifiableMap(result);
}

public Collection<Attribute> getAttributes() {
return attributeMap.values();
}

public String[] toQueryParams() {
List<String> queries = new LinkedList<>();
for (Attribute attr : attributeMap.values()) {
queries.add(String.format("%s=%s", attr.getCoRELinkParam(), attr.getValue()));
}
return queries.toArray(new String[queries.size()]);
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
String[] queryParams = toQueryParams();
for (int a = 0;a<queryParams.length;a++) {
sb.append(a < queryParams.length - 1 ? queryParams[a] + "&" : queryParams[a]);
}
return sb.toString();
}
}
Loading

0 comments on commit 81df3e0

Please sign in to comment.