/* 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 java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Map.Entry;
/**
* An iterator over the attributes in an element using the metadata to set the
* iteration order. If no metadata is provided the attributes will be treated
* as if they were all undeclared, and the iteration order will be the order
* they were added to the element.
*
*
*/
class AttributeIterator implements Iterator<Attribute> {
private enum Mode {DECLARED, UNDECLARED, DONE}
private final Element element;
private final ElementMetadata<?, ?> metadata;
private Iterator<AttributeKey<?>> metadataIterator;
private Iterator<Map.Entry<QName, Attribute>> attributeIterator;
private Attribute nextAttribute;
private Mode mode = Mode.DECLARED;
AttributeIterator(Element element, ElementMetadata<?, ?> metadata,
Map<QName, Attribute> attributes) {
this.element = element;
this.metadata = metadata;
this.metadataIterator = (metadata == null) ? null
: metadata.getAttributes().iterator();
this.attributeIterator = (attributes == null) ? null
: attributes.entrySet().iterator();
nextAttribute = findNextAttribute();
}
public boolean hasNext() {
return nextAttribute != null;
}
public Attribute next() {
if (nextAttribute == null) {
throw new NoSuchElementException("No remaining attributes");
}
Attribute retVal = nextAttribute;
nextAttribute = findNextAttribute();
return retVal;
}
public void remove() {
throw new UnsupportedOperationException(
"Removal not supported on attribute iterator");
}
/**
* Returns the next attribute in the iteration order or {@code null} if there
* are no remaining attributes.
*/
private Attribute findNextAttribute() {
Attribute next = null;
while (next == null && mode != Mode.DONE) {
switch(mode) {
case DECLARED:
next = findNextDeclaredAttribute();
break;
case UNDECLARED:
next = findNextUndeclaredAttribute();
break;
default:
break;
}
}
return next;
}
/**
* Finds the next declared attribute, or null if no more exist.
*/
private Attribute findNextDeclaredAttribute() {
if (metadataIterator != null) {
while (metadataIterator.hasNext()) {
AttributeKey<?> nextKey = metadataIterator.next();
if (ElementCreatorImpl.ATTRIBUTE_MARKER == nextKey) {
mode = Mode.UNDECLARED;
return null;
}
AttributeMetadata<?> attMeta = metadata.bindAttribute(nextKey);
if (!attMeta.isVisible()) {
continue;
}
Object value = attMeta.generateValue(element, metadata);
if (value != null) {
return new Attribute(nextKey, value);
}
}
// No more declared attributes, turn the iterator off.
metadataIterator = null;
}
// Check undeclared next.
mode = Mode.UNDECLARED;
return null;
}
/**
* Finds the next valid undeclared attribute in the map.
*/
private Attribute findNextUndeclaredAttribute() {
if (attributeIterator != null) {
while (attributeIterator.hasNext()) {
Entry<QName, Attribute> entry = attributeIterator.next();
Attribute attribute = entry.getValue();
if (isUndeclared(attribute.getAttributeKey())) {
return entry.getValue();
}
}
// No more attributes, turn the iterator off.
attributeIterator = null;
}
// Go back and check for any remaining declared attributes if needed.
mode = metadataIterator != null && metadataIterator.hasNext()
? Mode.DECLARED : Mode.DONE;
return null;
}
/**
* Returns true if the given attribute was not declared.
*/
private boolean isUndeclared(AttributeKey<?> key) {
return (metadata == null) || !metadata.isDeclared(key);
}
}