package org.itsnat.droid.impl.xmlinflater.shared.classtree;
import android.content.Context;
import org.itsnat.droid.AttrResourceInflaterListener;
import org.itsnat.droid.ItsNatDroidException;
import org.itsnat.droid.impl.browser.PageImpl;
import org.itsnat.droid.impl.dom.DOMAttr;
import org.itsnat.droid.impl.dom.DOMAttrRemote;
import org.itsnat.droid.impl.dom.DOMElement;
import org.itsnat.droid.impl.xmlinflater.AttrResourceContext;
import org.itsnat.droid.impl.xmlinflater.ClassDescMgr;
import org.itsnat.droid.impl.xmlinflater.XMLInflaterContext;
import org.itsnat.droid.impl.xmlinflater.XMLInflaterResource;
import org.itsnat.droid.impl.xmlinflater.shared.attr.AttrDesc;
import java.util.Map;
/**
* Created by Jose on 15/10/2015.
*/
public abstract class ClassDescResourceBased<TnativeResource,TattrCtx extends AttrResourceContext> extends ClassDesc<TnativeResource>
{
public ClassDescResourceBased(ClassDescMgr<? extends ClassDescResourceBased> classMgr, String tagName, ClassDescResourceBased<? super TnativeResource,TattrCtx> parentClass)
{
super(classMgr, tagName, parentClass);
}
@SuppressWarnings("unchecked")
public ClassDescMgr<ClassDescResourceBased> getClassDescMgr()
{
return (ClassDescMgr<ClassDescResourceBased>)classMgr;
}
@SuppressWarnings("unchecked")
public ClassDescResourceBased<TnativeResource,TattrCtx> getParentClassDescResourceBased()
{
return (ClassDescResourceBased<TnativeResource,TattrCtx>) getParentClassDesc(); // Puede ser null
}
//public abstract Class<TnativeResource> getDeclaredClass();
protected abstract TnativeResource createResourceNative(Context ctx);
public TnativeResource createRootResourceAndFillAttributes(DOMElement rootDOMElem, TattrCtx attrCtx)
{
@SuppressWarnings("unchecked")
XMLInflaterResource<TnativeResource> xmlInflater = attrCtx.getXMLInflaterResource();
TnativeResource rootResource = createResourceNative(xmlInflater.getContext());
xmlInflater.getInflatedXMLResource().setRootResource(rootResource); // Lo antes posible
fillResourceAttributes(rootResource, rootDOMElem, attrCtx);
return rootResource;
}
public TnativeResource createResourceAndFillAttributes(DOMElement domElement, TattrCtx attrCtx)
{
@SuppressWarnings("unchecked")
XMLInflaterResource<TnativeResource> xmlInflater = attrCtx.getXMLInflaterResource();
TnativeResource resource = createResourceNative(xmlInflater.getContext());
fillResourceAttributes(resource, domElement, attrCtx);
return resource;
}
public void fillResourceAttributes(TnativeResource resource, DOMElement domElement, TattrCtx attrCtx)
{
Map<String,DOMAttr> attribMap = domElement.getDOMAttributes();
if (attribMap != null)
{
for (Map.Entry<String,DOMAttr> entry : attribMap.entrySet())
{
DOMAttr attr = entry.getValue();
setAttribute(resource, attr, attrCtx);
}
}
}
public boolean setAttribute(final TnativeResource resource, final DOMAttr attr, final TattrCtx attrCtx)
{
// Devolvemos true si consideramos "procesado", esto incluye que sea ignorado o procesado custom
String namespaceURI = attr.getNamespaceURI();
String name = attr.getName(); // El nombre devuelto no contiene el namespace
String value = attr.getValue();
try
{
if (setAttributeThisClass(resource,attr,attrCtx))
return true;
XMLInflaterContext xmlInflaterContext = attrCtx.getXMLInflaterContext();
return processSetAttrCustom(resource, namespaceURI, name, value, xmlInflaterContext);
}
catch (Exception ex)
{
throw new ItsNatDroidException("Error setting attribute: " + namespaceURI + " " + name + " " + value + " in object " + resource, ex);
}
}
private boolean setAttributeThisClass(final TnativeResource resource, final DOMAttr attr, final TattrCtx attrCtx)
{
// Devolvemos true si consideramos "procesado", esto incluye que sea ignorado o procesado custom
if (!isInit()) init();
String namespaceURI = attr.getNamespaceURI();
String name = attr.getName(); // El nombre devuelto no contiene el namespace
//String value = attr.getValue();
if (isAttributeIgnored(resource,namespaceURI, name))
return true; // Se trata de forma especial en otro lugar
final AttrDesc<ClassDescResourceBased, TnativeResource, TattrCtx> attrDesc = this.<ClassDescResourceBased, TnativeResource, TattrCtx>getAttrDesc(namespaceURI, name);
if (attrDesc != null)
{
Runnable task = new Runnable()
{
@Override
public void run()
{
attrDesc.setAttribute(resource, attr, attrCtx);
}
};
if (DOMAttrRemote.isPendingToDownload(attr)) // Ver comentarios en la clase equivalente de drawables, layouts etc
AttrDesc.processDownloadTask((DOMAttrRemote) attr, task, attrCtx.getXMLInflaterContext());
else
task.run();
return true;
}
else
{
// Es importante recorrer las clases de abajo a arriba pues algún atributo se repite en varios niveles tal y como minHeight y minWidth
// y tiene prioridad la clase más derivada
ClassDescResourceBased<TnativeResource,TattrCtx> parentClass = getParentClassDescResourceBased();
if (parentClass != null)
{
return parentClass.setAttributeThisClass(resource, attr, attrCtx);
}
return false;
}
}
public boolean removeAttribute(TnativeResource resource, String namespaceURI, String name, TattrCtx attrCtx)
{
try
{
if (removeAttributeThisClass(resource,namespaceURI,name,attrCtx))
return true;
XMLInflaterContext xmlInflaterContext = attrCtx.getXMLInflaterContext();
return processRemoveAttrCustom(resource, namespaceURI, name, xmlInflaterContext);
}
catch(Exception ex)
{
throw new ItsNatDroidException("Error removing attribute: " + namespaceURI + " " + name + " in object " + resource, ex);
}
}
private boolean removeAttributeThisClass(TnativeResource resource, String namespaceURI, String name, TattrCtx attrCtx)
{
if (!isInit()) init();
if (isAttributeIgnored(resource,namespaceURI,name))
return true; // Se trata de forma especial en otro lugar
AttrDesc<ClassDescResourceBased,TnativeResource, TattrCtx> attrDesc = this.<ClassDescResourceBased,TnativeResource, TattrCtx>getAttrDesc(namespaceURI, name);
if (attrDesc != null)
{
attrDesc.removeAttribute(resource,attrCtx);
// No tiene mucho sentido añadir isPendingToDownload etc aquí, no encuentro un caso de que al eliminar el atributo el valor por defecto a definir sea remoto aunque sea un drawable lo normal será un "@null" o un drawable por defecto nativo de Android
return true;
}
else
{
ClassDescResourceBased<TnativeResource,TattrCtx> parentClass = getParentClassDescResourceBased();
if (parentClass != null)
{
return parentClass.removeAttributeThisClass(resource, namespaceURI, name, attrCtx);
}
return false;
}
}
private boolean processSetAttrCustom(TnativeResource resource, String namespaceURI, String name, String value, XMLInflaterContext xmlInflaterContext)
{
// No se encuentra opción de proceso custom
AttrResourceInflaterListener listener = xmlInflaterContext.getAttrResourceInflaterListener();
if(listener!=null)
{
PageImpl page = xmlInflaterContext.getPageImpl(); // Puede ser null
return listener.setAttribute(page, resource, namespaceURI, name, value);
}
return false;
}
private boolean processRemoveAttrCustom(TnativeResource resource, String namespaceURI, String name, XMLInflaterContext xmlInflaterContext)
{
// No se encuentra opción de proceso custom
AttrResourceInflaterListener listener = xmlInflaterContext.getAttrResourceInflaterListener();
if(listener!=null)
{
PageImpl page = xmlInflaterContext.getPageImpl(); // Puede ser null
return listener.removeAttribute(page, resource, namespaceURI, name);
}
return false;
}
public boolean isAttributeIgnored(TnativeResource resource,String namespaceURI, String name)
{
return false;
}
@Override
protected void init()
{
//initClass();
super.init();
}
}