/*
* Copyright 2003-2017 JetBrains s.r.o.
*
* 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 jetbrains.mps.extapi.model;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.model.SModel;
import java.util.function.BiConsumer;
/**
* PROVISIONAL API
*
* Captures auxiliary model capability to keep primitive attributes. Decouples clients that from specific SModel implementations,
* facilitates additional non-node values tracked along with a model. SModel implementation MPS uses for regular user models
* implements this interface
* <p/>
* Similar to {@code jetbrains.mps.persistence.PersistenceVersionAware} or {@link GeneratableSModel}.
* <p/>
* DESIGN NOTE: We expect attribute keys and values to be serialized and hence limit their kind to String.
* To facilitate other primitive values, we might overload {@link #setAttribute(String, String)} with proper value type and introduce
* respective getters (e.g. {@code getIntAttribute()}), if there's need to deal with values other than Strings.
*
* Perhaps, with the number of interfaces like this one growing, we shall consider {@code IAdaptable.adapt(Class)} mechanism, with Attribute holder object
* independent from SModel. What I don't like in the approach with distinct accessor object is that it's harder to control lifecycle and the moment it's
* accessed. With interface that extends SModel, lifecycle is the same as that of the model.
*
* @author Artem Tikhomirov
* @since 2017.1
*/
public interface ModelWithAttributes extends SModel {
/**
* Record extra data with a model
* @param key attribute identity
* @param value attribute value, or {@code null} do remove an attribute, if any.
*/
void setAttribute(@NotNull String key, @Nullable String value);
/**
* Retrieve extra model data.
* @param key attribute identity. There's no penalty if the key is unknown/unsupported with this model implementation
* @return {@code null} if there's no value for the key.
*/
@Nullable
String getAttribute(@NotNull String key);
/**
* Iterate over all available attributes. Generally, not present attributes are not reported, however, clients
* shall expect null values, and implementation may report missing/deleted attributes.
*
* For now, the contract is {@code action} shall not modify attributes, we might relax this in future
* (i.e. iterate over a copy of internal storage), if there are scenarios we find it handy.
*
* @param action action to perform for each key-value attribute pair
*/
void forEach(@NotNull BiConsumer<String, String> action);
/**
* @param key attribute identity
* @param defaultValue attribute value in case implementation doesn't know one.
* @return value of the attribute, if any, or {@code defaultValue} otherwise
*/
@Nullable
default String getAttribute(@NotNull String key, @Nullable String defaultValue) {
String value = getAttribute(key);
return value == null ? defaultValue : value;
}
}