/*
* 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 org.atteo.config.jaxb;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationEventLocator;
import org.eclipse.persistence.jaxb.IDResolver;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
/**
* Resolve Id references in the scope of the expected class.
*
* <p>
* @see <a href="http://weblogs.java.net/blog/2005/08/15/pluggable-ididref-handling-jaxb-20">Pluggable IdRef</a>
* </p>
*/
public class ScopedIdResolver extends IDResolver {
private static class Key {
private final Class<?> klass;
private final Object id;
public Key(Class<?> klass, Object id) {
this.klass = klass;
this.id = id;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Key other = (Key) obj;
if (klass != other.getClass()
&& (klass == null || !klass.equals(other.getKlass()))) {
return false;
}
if ((id == null) ? (other.getId() != null) : !id.equals(other.getId())) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 29 * hash + (klass != null ? klass.hashCode() : 0);
hash = 29 * hash + (id != null ? id.hashCode() : 0);
return hash;
}
public Object getId() {
return id;
}
public Class<?> getKlass() {
return klass;
}
}
private static class NotUniqueIdValidationEvent implements ValidationEvent {
private final Key key;
public NotUniqueIdValidationEvent(Key key) {
this.key = key;
}
@Override
public int getSeverity() {
return ValidationEvent.FATAL_ERROR;
}
@Override
public String getMessage() {
return "Cannot resolve XmlIDREF because pair [" + key.klass + ", id = \"" + key.id + "\"] is not unique";
}
@Override
public Throwable getLinkedException() {
return null;
}
@Override
public ValidationEventLocator getLocator() {
return new ValidationEventLocator() {
@Override
public URL getURL() {
return null;
}
@Override
public int getOffset() {
return 0;
}
@Override
public int getLineNumber() {
return -1;
}
@Override
public int getColumnNumber() {
return -1;
}
@Override
public Object getObject() {
return null;
}
@Override
public Node getNode() {
return null;
}
};
}
}
private enum Values { NON_UNIQUE };
private final Map<Key, Object> map = new HashMap<>();
private ValidationEventHandler eventHandler = null;
@Override
public void startDocument(ValidationEventHandler eventHandler) throws SAXException {
super.startDocument(eventHandler);
this.eventHandler = eventHandler;
}
@Override
public void endDocument() throws SAXException {
map.clear();
eventHandler = null;
super.endDocument();
}
@Override
public void bind(final Object id, final Object object) throws SAXException {
Class<?> klass = object.getClass();
while (klass != Object.class) {
Key key = new Key(klass, id);
Object value = map.get(key);
if (value != null) {
if (value != Values.NON_UNIQUE) {
map.put(key, Values.NON_UNIQUE);
}
} else {
map.put(key, object);
}
klass = klass.getSuperclass();
}
}
@Override
public Callable<?> resolve(final Object id, @SuppressWarnings("rawtypes") final Class targetType)
throws SAXException {
return () -> {
Key key = new Key(targetType, id);
Object value = map.get(key);
if (value == Values.NON_UNIQUE) {
ValidationEvent event = new NotUniqueIdValidationEvent(key);
if (!eventHandler.handleEvent(event)) {
throw new SAXException(event.getMessage());
}
}
return value;
};
}
@Override
public Callable<?> resolve(Map<String, Object> id, Class type) throws SAXException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void bind(Map<String, Object> id, Object obj) throws SAXException {
throw new UnsupportedOperationException("Not supported yet.");
}
}