All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.naturzukunft.rdf4j.ommapper.Converter Maven / Gradle / Ivy

package de.naturzukunft.rdf4j.ommapper;

import static org.eclipse.rdf4j.model.util.Values.iri;

import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;

import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Literal;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.Rio;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Converter  {

	private Class cls;

	public Converter(Class cls) {
		this.cls = cls;
	}

	public Optional fromModel(IRI subject, Model model) {
		log.debug("->fromModel("+subject+")");
		Constructor constructor = getConstructor(cls);
		if(model.filter(subject, null, null).size() == 0) {
			log.warn("no statements for " + subject);
			log.info("<- fromModel(...");
			return Optional.empty();
		}
		
		StringWriter sw = new StringWriter();
		Rio.write(model, sw, RDFFormat.TURTLE);		
		log.debug("model: " + sw.toString());
		
		try {
			T instance = constructor.newInstance();
			Map fieldsByIri = getFieldsByIri(cls);
			List nonNullFileds = getNonNullFields(cls);
			Model filtered = model.filter(subject, null, null);
			log.debug("model filtered by subject. Statements after filtering: " + filtered.size());
			filtered.forEach(stmt->{
				log.debug("processing statement: " + stmt);
				IRI predicate = stmt.getPredicate();
				Field field = fieldsByIri.get(predicate);
				log.trace("fieldsByIri.get("+predicate+") -> " + field );
				Value stmtObject = stmt.getObject();			
				if(field!=null) {
					try {
						log.debug("matching field (reflection): ("+field.getName()+ ")  literal ("+stmtObject+") type ("+field.getType().getName()+")" );
						
						if (Collection.class.isAssignableFrom(field.getType())) {							
							handleSetForCollection(instance, field, stmtObject);
						} else 
						
						if(stmtObject.isLiteral()) {
							setValue(instance, field, (Literal)stmtObject);
						} else if(stmtObject.isIRI()) {
							if (Collection.class.isAssignableFrom(field.getType())) { // TODO remove							
								handleSetForCollection(instance, field, stmtObject);
							}
							else if(stmtObject.isResource()) {
								Iri iriAnnotation = field.getAnnotation(Iri.class);
								log.debug("iriAnnotation of " + field.getName() + ": " + iriAnnotation);
								if(iriAnnotation!=null && !field.getType().equals(org.eclipse.rdf4j.model.IRI.class)) {
									log.debug("field is reference to: (DELETE ME, just for debugging reasons) ");
									log.debug("field is reference to: " + iriAnnotation.value());
									
									Converter converter = new Converter(field.getType());
									log.debug("calling converter for subtype: " + field.getName() + "("+field.getType()+")");
									Optional subObjectOptional = converter.fromModel((IRI)stmtObject, model);
									if(subObjectOptional.isPresent()) {
										field.set(instance, subObjectOptional.get());
									} else {
										log.warn("Optional.empty() ->  " + iriAnnotation.value() + "(" +stmtObject +")");
									}
								} else {
									log.info("isn't it a bug to set a value here ?");
									field.set(instance, iri(stmtObject.toString()));	
								}
							}
						}
					} catch (Exception e) {
						if(e instanceof NonNullException) {
							throw (NonNullException)e;
						} else {
							String name = null;
							if(field != null) {
								name = field.getName();
							}
							throw new RuntimeException("error setting value of field " + name, e);
						}
					}
				}
			});
			
			setSubject(subject, instance);
			
			nonNullFileds.stream().forEach(it-> {
				try {
					if(it.get(instance) == null) {
						throw new NonNullException(it.getName()  + " is annotated with NonNull, but is null!");
					}
				} catch (IllegalArgumentException | IllegalAccessException e) {
					throw new RuntimeException("error checking NonNull" + it, e);
				}
			});

			log.debug("<- fromModel(...");
			return Optional.ofNullable(instance);
		} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
			log.info("<- fromModel(...");
			throw new RuntimeException("error instantiating constructor", e1);
		}
	}

	private void setSubject(IRI subject, T instance) throws IllegalAccessException {
		try {
			List fields = getFields(instance.getClass());
			Field subjectField = fields.stream().filter(field->field.getName().equals("subject")).findFirst().get();
			subjectField.set(instance, subject);
		} catch (SecurityException e) {
			throw new RuntimeException("error setting subject", e);
		}
	}

	private Constructor getConstructor(Class cls) {
		Constructor constructor;
		try {
			constructor = cls.getConstructor();
		} catch (NoSuchMethodException e1) {
			throw new RuntimeException("NoArg Constructor is mandatory! " + cls.getName());
		}
		return constructor;
	}

	private void handleSetForCollection(T instance, Field field, Value object)
			throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
		Object collection = field.get(instance);
		if(collection==null) {
			field.set(instance, new HashSet<>());
			collection = field.get(instance);
		}
		
		java.lang.reflect.Method add = Collection.class.getDeclaredMethod("add",Object.class);
		
		if( object.isLiteral() )
		{
			String value = object.stringValue();
			add.invoke(collection, value);	
		} else if( object.isIRI()) {
			String value = object.stringValue();
			add.invoke(collection, iri(value));
		}
	}
	
	private List getFields(Class cls) {
		List fields =getAllFields(new LinkedList(), cls);
		fields.forEach(field->field.setAccessible(true));
		return fields;
	}

	private List getAllFields(List fields, Class type) {
	    fields.addAll(Arrays.asList(type.getDeclaredFields()));

	    if (type.getSuperclass() != null) {
	        getAllFields(fields, type.getSuperclass());
	    }

	    return fields;
	}
	
	private void setValue(T instance, Field field, Literal object) throws IllegalArgumentException, IllegalAccessException {
		switch (field.getType().getName()) {
					case "java.lang.Boolean":
					case "boolean":
						field.set(instance, object.booleanValue());
						break;
					case "java.lang.Byte":
					case "byte":
						field.set(instance, object.byteValue());
						break;
//					case "char":
//					case "java.lang.Char":
//						Character chatr = Character.valueOf(object.byteValue());
//						field.set(instance, chatr);
//						break;
					case "java.lang.Double":
					case "double":
						field.set(instance, object.doubleValue());
						break;
					case "java.lang.Float":
					case "float":
						field.set(instance, object.floatValue());
						break;
					case "java.math.BigInteger":
						field.set(instance, object.integerValue());
						break;
					case "java.lang.Integer":
					case "int":
						field.set(instance, object.intValue());
						break;
					case "java.lang.Long":
					case "long":
						field.set(instance, object.longValue());
						break;
					case "java.lang.Short":
					case "short":
						field.set(instance, object.shortValue());
						break;
					case "java.lang.String":
						field.set(instance, object.stringValue());
						break;						
					case "java.time.LocalDateTime":
						XMLGregorianCalendar xmlGregorianCalendar = object.calendarValue();
						GregorianCalendar gregorianCalendar = xmlGregorianCalendar.toGregorianCalendar();
						TimeZone tz = gregorianCalendar.getTimeZone();
				        ZoneId zoneId = tz.toZoneId();
				        Instant instant = gregorianCalendar.toInstant();
						LocalDateTime localDate = LocalDateTime.ofInstant(instant, zoneId);
						field.set(instance, localDate);
						break;						
					case "java.time.Duration":
						try {
							Duration d = DatatypeFactory.newInstance().newDuration(object.stringValue());
							java.time.Duration timeDuration = java.time.Duration.ofMillis(d.getTimeInMillis(Calendar.getInstance()));
							field.set(instance, timeDuration);
						} catch (DatatypeConfigurationException e) {
							log.error("error setting duration from value " + object.stringValue(), e);
						}
						break;						
		default:
			log.warn("setValue DEFAULT is called for field ("+field.getName()+ ")  literal ("+object+") type ("+field.getType().getName()+")" );
//			Iri iriAnnotation = field.getAnnotation(Iri.class);
//			if(iriAnnotation!=null) {
//				log.debug("field is reference to: " + iriAnnotation.value());
//				fromModel(field.getType(), iriAnnotation.value(), model);
//			} else {
				field.set(instance, object.stringValue());
//			}
			break;
		}
	}
	
	private Map getFieldsByIri(Class cls) {
		Map response = new HashMap<>();
		List fields = getFields(cls);
		for (Field field : fields) {
			log.trace("getFieldsByIri(): processing field " + field.getName());
			List asList = Arrays.asList(field.getAnnotations());
			Optional annotation = asList.stream().filter(anno->anno.annotationType().equals(Iri.class)).findFirst();
			if(annotation.isPresent()) {
				String fieldIri = field.getAnnotation(Iri.class).value();
				response.put(iri(fieldIri), field);
			}
		}		
		return response;
	}
	
	private List getNonNullFields(Class cls) {
		List response = new ArrayList<>();
		List fields = getFields(cls);
		for (Field field : fields) {
			List asList = Arrays.asList(field.getAnnotations());
			Optional annotation = asList.stream().filter(anno->anno.annotationType().equals(de.naturzukunft.rdf4j.ommapper.NonNull.class)).findFirst();
			if(annotation.isPresent()) {
				response.add(field);
				log.trace("getNonNullFields(): processing field " + field.getName() + " NoneNull -> TRUE");
			}
		}		
		return response;
	}
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy