SnmpMibDispatch.java

package eu.linuxengineering.snmp;

import java.util.Map.Entry;

import eu.javaexperience.collection.map.KeyVal;
import eu.javaexperience.log.JavaExperienceLoggingFacility;
import eu.javaexperience.log.LogLevel;
import eu.javaexperience.log.Loggable;
import eu.javaexperience.log.Logger;
import eu.javaexperience.log.LoggingTools;
import eu.javaexperience.reflect.Mirror;

import java.util.Arrays;
import java.util.SortedMap;
import java.util.TreeMap;

import net.sf.snmpadaptor4j.api.AttributeAccessor;
import net.sf.snmpadaptor4j.api.SnmpMib;
import net.sf.snmpadaptor4j.object.SnmpOid;

public class SnmpMibDispatch extends SnmpDispatchNode implements SnmpMib
{
	protected static final Logger LOG = JavaExperienceLoggingFacility.getLogger(new Loggable("SnmpMibDispatch"));
	
	//protected boolean listIntermediateNodes;
	
	public void addNode(Integer id, SnmpNode node)
	{
		subNodes.put(id, node);
	}
	
	public SnmpNode getNode(Integer id)
	{
		return subNodes.get(id);
	}
	
	@Override
	public AttributeAccessor find(SnmpOid oid)
	{
		LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "SnmpMibDisaptch.find(this: `%s`, oid: `%s`)", this, oid);
		SnmpPathDispatch dispatch = new SnmpPathDispatch(oid, 0);
		Entry<Integer, SnmpNode> node = new KeyVal(-1, this);
		
		int prevIndex = -1;
		do
		{
			if(prevIndex == dispatch.getCurrentPathIndex())
			{
				LoggingTools.tryLogFormat
				(
					LOG,
					LogLevel.ERROR,
					"SnmpMibDisaptch.find: Snmp dispatch looping ocurred when dispatching oid: `%s` at node: `%s`, node: `%s`",
					oid,
					prevIndex,
					node
				);
				throw new RuntimeException("Snmp dispatch loop exception oid: "+oid+", node: "+node+" index: "+prevIndex);
			}
			prevIndex = dispatch.getCurrentPathIndex();
			
			Integer id = dispatch.getCurrentPathOid();
			
			boolean last = dispatch.isLastDispatch();
			
			node = node.getValue().getSubNodeGte(dispatch);
			
			if(null == node)
			{
				return null;
			}
			
			//in this case we looking for an exact match
			if(!node.getKey().equals(id))
			{
				return null;
			}
			
			if(last && node.getKey().equals(id))
			{
				return node.getValue().getAccessor();
			}
		}
		while(dispatch.hasNexOidPath());
		
		return null;
	}

	public AttributeAccessor next(SnmpOid oid)
	{
		LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "SnmpMibDisaptch.next(this: `%s`, oid: `%s`)", this, oid);
		SnmpNode ret = null;
		try
		{
			SnmpPathDispatch dispatch = new SnmpPathDispatch(oid, 0);
			ret = findNext(this, dispatch, false);
		}
		catch(Exception e)
		{
			LoggingTools.tryLogFormatException(LOG, LogLevel.ERROR, e, "SnmpMibDisaptch.next(this: `%s`, oid: `%s`): Exception: ", this, oid);
			Mirror.propagateAnyway(e);
		}
		LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "SnmpMibDisaptch.next(this: `%s`, oid: `%s`): found next: `%s`", this, oid, ret);
		return null == ret?null:ret.getAccessor();
	}
	
	protected static SnmpPathDispatch modifyDispatchFrom
	(
		SnmpPathDispatch proto,
		int at,
		int newValue,
		int index
	)
	{
		int[] path = Arrays.copyOf(proto.oids, at+1);
		path[at] = newValue;
		return new SnmpPathDispatch(path, index);
	}
	
	protected static final SnmpNode getDepthFirst(SnmpPathDispatch from, SnmpNode node)
	{
		int[] dsp = Arrays.copyOf(from.oids, from.index+1);
		dsp[from.index] = -1;
		SnmpPathDispatch disp = new SnmpPathDispatch(dsp, from.index);
		if(node.hasSubNodes())
		{
			//disp.setPathIndex(0);
			Entry<Integer, SnmpNode> next = node.getSubNodeGte(disp);
			disp.jumpPreviousNode();
			if(null != next)
			{
				dsp[from.index] = next.getKey();
				disp.jumpNextNode();
				SnmpNode ret = getDepthFirst(disp, next.getValue());
				if(null == ret && !next.getValue().hasSubNodes())
				{
					return next.getValue();
				}
				return ret;
			}
			else
			{
				return null;
			}
		}
		
		return node;
	}
	
	protected static final SnmpNode findNext
	(
		SnmpNode node,
		SnmpPathDispatch dispatch,
		boolean exact
	)
	{
		int pathIndex = dispatch.getCurrentPathIndex();
		Integer req = dispatch.getCurrentPathOid();
		
		Entry<Integer, SnmpNode> n = node.getSubNodeGte(dispatch);
		
		//if we found only a bigger one
		if(n != null)
		{
			if(n.getKey() > req)
			{
				return getDepthFirst
				(
					modifyDispatchFrom(dispatch, pathIndex, n.getKey(), pathIndex+1),
					n.getValue()
				);
			}
		}
		
		SnmpNode next = null == n?null:n.getValue();
		
		if(null == next)
		{
			return null;
		}
		
		//the very last node to dispatch that we found 
		if(dispatch.isInvalid())
		{
			if(next.hasSubNodes())
			{
				return getDepthFirst
				(
					modifyDispatchFrom(dispatch, pathIndex, n.getKey(), pathIndex+1),
					//dispatch,
					next
				);
			}
			else
			{
				if(exact)
				{
					return next;
				}
				return findNext
				(
					node,
					modifyDispatchFrom
					(
						dispatch,
						pathIndex,
						dispatch.oids[pathIndex]+1,
						pathIndex
					),
					true
				);
			}
		}
		
		next = findNext(next, dispatch, false);
		
		if(null == next)
		{
			return findNext
			(
				node,
				modifyDispatchFrom
				(
					dispatch,
					pathIndex,
					dispatch.oids[pathIndex]+1,
					pathIndex
				),
				true //TODO
			);
		}
		
		return next;
	}

	@Override
	public SortedMap<SnmpOid, AttributeAccessor> nextSet(SnmpOid oid)
	{
		int next = 30;
		//a simple reuse method
		SortedMap<SnmpOid, AttributeAccessor> ret = new TreeMap<>();
		
		while(--next > 0)
		{
			AttributeAccessor req = next(oid);
			if(null == req)
			{
				break;
			}
			oid = req.getOid();
			ret.put(oid, req);
		}
		
		return ret;
	}
}