blob: 72ce62a9f3afd973642e2899c57f06ca2c3cde60 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.sis.style.se1;
import java.awt.Color;
import java.util.Arrays;
import java.util.Objects;
import jakarta.xml.bind.annotation.XmlTransient;
import org.opengis.util.InternationalString;
// Specific to the main branch:
import org.apache.sis.filter.Expression;
/**
* Base class of all style objects.
* This base class cannot be extended directly.
* Instead, one of the subclasses can be extended.
*
* @author Martin Desruisseaux (Geomatys)
*
* @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
*/
@XmlTransient
public abstract class StyleElement<R> implements Cloneable {
/**
* The factory to use for creating expressions and child elements.
* This is typically the same factory as the one used for creating this element.
*
* @see FeatureTypeStyle#FACTORY
* @see CoverageStyle#FACTORY
*/
protected final StyleFactory<R> factory;
/**
* Creates a new style element.
* Intentionally restricted to this package because {@link #properties()} is package-private.
*
* @param factory the factory to use for creating expressions and child elements.
*/
StyleElement(final StyleFactory<R> factory) {
this.factory = Objects.requireNonNull(factory);
}
/**
* Creates a shallow copy of the given object.
* For a deep copy, see {@link #clone()} instead.
*
* @param source the object to copy.
*/
StyleElement(final StyleElement<R> source) {
factory = source.factory;
}
/**
* Creates a style element for XML unmarshalling.
* <em>This constructor is unsafe</em> and should be used only by JAXB reflection.
*
* @todo Allow the factory to be set according the parent {@link AbstractStyle} being unmarshalled.
* We will need to use {@link ThreadLocal}.
*/
StyleElement() {
factory = null; // TODO
}
/**
* Returns a literal for the given value.
* This is a convenience method for use with setter methods that expect an expression.
*
* @param <E> type of value.
* @param value the value for which to return a literal, or {@code null} if none.
* @return literal for the given value, or {@code null} if the given value was null.
*/
public final <E> Expression<R,E> literal(final E value) {
return (value == null) ? null : factory.filterFactory.literal(value);
}
/**
* Returns the given expression if non-null, or literal {@code true} otherwise.
* This is a convenience method for the implementation of getter methods when
* a default value exists.
*
* @param value the value for which to apply a default value if {@code null}.
* @return the given value if non-null, or {@code true} literal otherwise.
*/
protected final Expression<R,Boolean> defaultToTrue(final Expression<R,Boolean> value) {
return (value != null) ? value : factory.enabled;
}
/**
* Returns the given expression if non-null, or literal {@code false} otherwise.
* This is a convenience method for the implementation of getter methods when
* a default value exists.
*
* @param value the value for which to apply a default value if {@code null}.
* @return the given value if non-null, or a {@code false} literal otherwise.
*/
protected final Expression<R,Boolean> defaultToFalse(final Expression<R,Boolean> value) {
return (value != null) ? value : factory.disabled;
}
/**
* Returns the given expression if non-null, or literal {@code 0.0} otherwise.
* This is a convenience method for the implementation of getter methods when
* a default value exists.
*
* @param value the value for which to apply a default value if {@code null}.
* @return the given value if non-null, or {@code 0.0} literal otherwise.
*/
protected final Expression<R, ? extends Number> defaultToZero(final Expression<R, ? extends Number> value) {
return (value != null) ? value : factory.zero;
}
/**
* Returns the given expression if non-null, or literal {@code 0.5} otherwise.
* This is a convenience method for the implementation of getter methods when
* a default value exists.
*
* @param value the value for which to apply a default value if {@code null}.
* @return the given value if non-null, or {@code 0.5} literal otherwise.
*/
protected final Expression<R, ? extends Number> defaultToHalf(final Expression<R, ? extends Number> value) {
return (value != null) ? value : factory.half;
}
/**
* Returns the given expression if non-null, or literal {@code 1.0} otherwise.
* This is a convenience method for the implementation of getter methods when
* a default value exists.
*
* @param value the value for which to apply a default value if {@code null}.
* @return the given value if non-null, or {@code 1.0} literal otherwise.
*/
protected final Expression<R, ? extends Number> defaultToOne(final Expression<R, ? extends Number> value) {
return (value != null) ? value : factory.one;
}
/**
* Returns the opacity of the alpha value of the given color.
* If the color is totally opaque, then this method returns {@code null}.
*
* @param color color from which to get the opacity.
* @return opacity derived from the alpha value of the color, or {@code null} if totally opaque.
*/
final Expression<R, ? extends Number> opacity(final Color color) {
final int alpha = color.getAlpha();
return (alpha != 255) ? literal(alpha / 256d) : null;
// Divide by 256 instead of 255 in order to get round numbers for alpha values 64, 128, etc.
}
/**
* Returns all properties contained in the subclasses.
* This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
*
* @return all properties.
*/
abstract Object[] properties();
/**
* Returns a hash code value for this object.
*
* @return a hash code value for this object.
*/
@Override
public int hashCode() {
return getClass().hashCode() + Arrays.hashCode(properties());
}
/**
* Compares this element with the given object for equality.
*
* @param obj the other object to compare with this.
* @return whether the other object is equal to this.
*/
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
return (obj != null) && (obj.getClass() == getClass()) &&
Arrays.equals(properties(), ((StyleElement) obj).properties());
}
/**
* Returns a deep clone of this object. All style elements are cloned,
* but expressions are not on the assumption that they are immutable.
* ISO 19115 metadata and {@link InternationalString} members, if any,
* are not cloned neither in current Apache SIS version.
*
* @return a clone of this element.
*/
@Override
@SuppressWarnings("unchecked")
public StyleElement<R> clone() {
try {
return (StyleElement<R>) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e); // Should never happen since we are cloneable.
}
}
}