/* Copyright (c) 2008 Google Inc.
*
* 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 com.google.gdata.model;
import com.google.gdata.model.Metadata.VirtualValue;
/**
* An immutable transform on a piece of metadata. This contains all of the
* base metadata fields, every value is optional (anything may be null).
*
*
*/
abstract class Transform {
// Our final, nullable fields.
private final QName name;
private final Boolean required;
private final Boolean visible;
private final VirtualValue virtualValue;
private final TransformKey source;
private final Path path;
private final boolean isMoved;
/**
* Constructs an empty transform with all null values. This should only be
* used by subclasses. See {@link AttributeTransform#EMPTY} and
* {@link ElementTransform#EMPTY} for the appropriate empty transforms.
*/
Transform() {
this.name = null;
this.required = null;
this.visible = null;
this.virtualValue = null;
this.source = null;
this.path = null;
this.isMoved = false;
}
/**
* Constructs a transform for the declared metadata described by the given
* creator. This constructor is only for use by subclasses, callers should
* use either {@link AttributeTransform#create(AttributeCreatorImpl)} or
* {@link ElementTransform#create(ElementCreatorImpl)} to build transforms
* from their builders.
*/
Transform(MetadataCreatorImpl creator) {
this.name = creator.getName();
this.required = creator.getRequired();
this.visible = creator.getVisible();
this.virtualValue = creator.getVirtualValue();
this.source = creator.getSource();
this.path = creator.getPath();
this.isMoved = creator.isMoved();
}
/**
* Construct a composite transform out of the given parts. Transforms are
* combined by allowing transforms later in the parts to override values
* provided earlier in the iterable.
*/
Transform(Iterable<? extends Transform> parts) {
QName compositeName = null;
Boolean compositeRequired = null;
Boolean compositeVisible = null;
VirtualValue compositeVirtualValue = null;
TransformKey compositeSource = null;
Path compositePath = null;
boolean compositeMoved = false;
for (Transform part : parts) {
if (part.name != null) {
compositeName = part.name;
}
if (part.required != null) {
compositeRequired = part.required;
}
if (part.visible != null) {
compositeVisible = part.visible;
}
if (part.virtualValue != null) {
compositeVirtualValue = part.virtualValue;
}
if (part.source != null) {
compositeSource = part.source;
}
if (part.path != null) {
compositePath = part.path;
}
if (part.isMoved) {
compositeMoved = true;
}
}
this.name = compositeName;
this.required = compositeRequired;
this.visible = compositeVisible;
this.virtualValue = compositeVirtualValue;
this.source = compositeSource;
this.path = compositePath;
this.isMoved = compositeMoved;
}
/**
* Constructs a transform out of a base transform and a source transform.
* This differs from the {@link #Transform(Iterable)} constructor by only
* using certain values from the source. The differences are:
* <ul>
* <li>Requiredness is based only on the transform, not on the source.</li>
* <li>The path is pulled only from the transform, not from the source. If
* the source also has a path it will be resolved during parsing/generation as
* appropriate.<li>
* <li>Ignores if the source is marked as being moved.</li>
* <li>Explicitly nulls the {@code source} field, as the source has already
* been taken into account.</li>
*/
Transform(Transform transform, Transform source) {
this.name = first(transform.name, source.name);
this.required = transform.required;
this.visible = first(transform.visible, source.visible);
this.virtualValue = first(transform.virtualValue, source.virtualValue);
this.path = transform.path;
this.isMoved = transform.isMoved;
// In this special case we've included source information, so we get rid
// of the source variable so we don't try to include it again.
this.source = null;
}
/**
* Returns the first non-null value, or null if all were null.
*/
static <T> T first(T... ts) {
for (T t : ts) {
if (t != null) {
return t;
}
}
return null;
}
QName getName() {
return name;
}
Boolean getRequired() {
return required;
}
Boolean getVisible() {
return visible;
}
VirtualValue getVirtualValue() {
return virtualValue;
}
TransformKey getSource() {
return source;
}
Path getPath() {
return path;
}
boolean isMoved() {
return isMoved;
}
boolean isEmpty() {
return name == null
&& required == null
&& visible == null
&& virtualValue == null
&& source == null
&& path == null
&& !isMoved;
}
}