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

CompilerRuntime.HashStorage Maven / Gradle / Ivy

There is a newer version: 1.7.3
Show newest version
package CompilerRuntime;

/*	
 * HashStorage.java  	$Revision: 243 $
 * 
 * Copyright (C) 2005-2007 Roozbeh Farahbod 
 * 
 * Last modified by $Author: rfarahbod $ on $Date: 2011-03-29 02:05:21 +0200 (Di, 29 Mrz 2011) $.
 *
 * Licensed under the Academic Free License version 3.0 
 *   http://www.opensource.org/licenses/afl-3.0.php
 *   http://www.coreasm.org/afl-3.0.php
 *
 */

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.coreasm.engine.absstorage.ElementList;
import org.coreasm.engine.absstorage.AbstractUniverse;
import org.coreasm.engine.absstorage.BackgroundElement;
import org.coreasm.engine.absstorage.FunctionElement;
import org.coreasm.engine.absstorage.Element;
import org.coreasm.engine.absstorage.BooleanElement;
import org.coreasm.engine.absstorage.IdentifierNotFoundException;
import org.coreasm.engine.absstorage.Location;
import org.coreasm.engine.absstorage.MapFunction;
import org.coreasm.engine.absstorage.UnmodifiableFunctionException;
import org.coreasm.engine.absstorage.Update;
import org.coreasm.engine.absstorage.NameConflictException;
import org.coreasm.engine.absstorage.UniverseElement;
import org.coreasm.engine.absstorage.InvalidLocationException;
import org.coreasm.engine.EngineError;
import org.coreasm.engine.absstorage.NameElement;
import org.coreasm.engine.interpreter.InitAgent;

/** 
 *	This is an implementation of the AbstractStorage interface that
 *  uses a HashState.
 *   
 *  @author  Roozbeh Farahbod
 *  
 */
public class HashStorage implements AbstractStorage {

	private static long lastStateId = 1000000;
	
	
	/** The state of the simulated machine. */
	private State state = null;
	// !!! IMPORTANT !!!
	// This state object (defined above) is used to implement the State 
	// interface. Developers SHOULD NOT refer to this object anywhere 
	// in this class (except in clearState and the State interface methods) 
	// and instead should call AbstractStorage methods; 
	// e.g., using this.setContent(...) instead of state.setContent(...).

	/** Link to the ControlAPI module. */
	private final Runtime runtime;

	/** Stack of update sets 
	 * 
	 * We keep updates as map of locations to values (per interpreter thread) 
	 * to increase performance.
	 */
	private final ThreadLocal>> updateStack;
	
	/** Cache for monitored function values */
	private final ConcurrentMap monitoredCache;
	
	/** Indicates if there is any state in the stack. */
	private ThreadLocal stateStacked;

	/** keeps the last inconsistent updates */
	private UpdateList lastInconsistentUpdates; 
	
	/** Creates a new HashStorage. */
	public HashStorage(Runtime runtime) {
		this.runtime = runtime;
		updateStack = new ThreadLocal>>() {
	         protected Stack> initialValue() {
	             return new Stack>();
	         }
		};
		stateStacked = new ThreadLocal() {
	         protected Boolean initialValue() {
	             return false;
	         }
		};
		monitoredCache = new ConcurrentHashMap();
		lastInconsistentUpdates = null;
		
		// the following line is commented out by Roozbeh Farahbod on 03-Oct-2006
		// the idea is not to duplicate state initialization which is done 
		// by initAbstractStorage()
		// clearState();
		
		// instead of that, we have
		state = new HashState();
	}
	
	/**
	 * Returns the currently stacked updates.
	 * @return the currently stacked updates
	 */
	public Map getStackedUpdates() {
		Stack> updateStack = getUpdateStack();
		if (updateStack.isEmpty())
			return Collections.emptyMap();
		
		Map stackedUpdates = new HashMap();
		for (Map stackedUpdate : updateStack)
			stackedUpdates.putAll(stackedUpdate);
		
		return stackedUpdates;
	}
	
	private Stack> getUpdateStack() {
		return updateStack.get();
	}
	
	private boolean isStateStacked() {
		return stateStacked.get();
	}
	
	private void setStateStackedFlag(boolean value) {
		stateStacked.set(value);
	}
	
	public void initAbstractStorage(CompilerRuntime.Rule initRule) {
		//clearState();
		
        runtime.getScheduler().setStepCount(0);
        
        try {
        	UniverseElement agentsuniverse = new UniverseElement();
        	Element initagent = new InitAgent();
        	agentsuniverse.setValue(initagent, BooleanElement.TRUE);
        	List arglist = new ArrayList();
        	arglist.add(initagent);
        	Location loc = new Location(CompilerRuntime.AbstractStorage.PROGRAM_FUNCTION_NAME, arglist);
        	try {
				this.setValue(loc, initRule);
			} catch (InvalidLocationException e) {
				//should never happen aswell
				e.printStackTrace();
			}
        	
			addUniverse(AbstractStorage.AGENTS_UNIVERSE_NAME, agentsuniverse);
			
		} catch (NameConflictException e) {
			//there should never be a name conflict
			e.printStackTrace();
		}
        
		//NOTE: insertion of initial elements is handled elsewhere
	}
	
	public synchronized void fireUpdateSet(UpdateList ul) throws InvalidLocationException {
		// Cannot fire updates while state stack is not empty.
		// Doing this check will allow us to bypass calling setValue(...)
		if (isStateStacked()) 
			throw new EngineError("Cannot fire updates when the state stack is not empty.");

		
		//System.out.println("firing update set:");
		//TODO this should be done in a transactional fashion
		for (Iterator it = ul.iterator(); it.hasNext(); ) {
			Update u = it.next();
			//System.out.println(u.toString());
			if(u.action.equals(Update.UPDATE_ACTION)){
				state.setValue(u.loc, u.value);
			}
		}
		monitoredCache.clear();
	}

	//public Element getChosenProgram(Element agent) {
	//	//TODO: implement if necessary
	//}

//	/**
//	 * @see org.coreasm.engine.absstorage.AbstractStorage#getState()
//	 */
//	public State getState() {
//		return this;
//	}
//
//	/** 
//	 * @see org.coreasm.engine.absstorage.AbstractStorage#setState(org.coreasm.engine.absstorage.State)
//	 */
//	public void setState(State newState) {
//		if (!stateStacked) 
//			this.state = newState;
//		else
//			throw new EngineError("Cannot set state when the state stack is not empty.");
//	}
//
	public Element getValue(Location l) throws InvalidLocationException {
		Element e = null;
		FunctionElement f = this.getFunction(l.name);
		
		if (f != null) {
			// Check if a monitored function is being probed
			if (f.getFClass() == FunctionElement.FunctionClass.fcMonitored) {
				// To make keep monitored functions consistent in one state, use caching
				if (monitoredCache.containsKey(l)) 
					return monitoredCache.get(l);
			}
		}

		e = this.getValueOverStack(l);
		
		if (f == null) {
			if (e == null)
				// if there is no such function and no new value for this location
				// is added in the stack (e.g., as part of a sequence) then there 
				// is a problem
				throw new InvalidLocationException("Location " + l + " does not exists.");
		} else {
			if (e == null) 
				e = Element.UNDEF;

			if (f.getFClass() == FunctionElement.FunctionClass.fcMonitored)
				monitoredCache.put(l, e);
		}
			
		return e;
	}

	public synchronized void setValue(Location l, Element v) throws InvalidLocationException {
		if (!isStateStacked()) 
			state.setValue(l, v);
		else
			throw new EngineError("Cannot set state content when the state stack is not empty.");
	}

	/*
	 * Gets the value of a location possibly going through the stack of states.
	 */
	private Element getValueOverStack(Location loc) throws InvalidLocationException {
		if (!isStateStacked()) 
			return state.getValue(loc);
		else {
			Stack> updateStack = getUpdateStack();

			// Looking through the stack from the top...
			// This relies on the Vector implementation of the stack
			Map um = null;
			for (int i=updateStack.size()-1; i >= 0; i--) {
				um = updateStack.get(i);
				// Look in all the update multisets
				Element value = um.get(loc);
				if (value != null)
					return value;
			}
			return state.getValue(loc);
		}
	}

	public void aggregateUpdates() {	
		UpdateList updateInsts = runtime.getScheduler().getUpdateInstructions();
		UpdateList tempUpdateSet = null;

		tempUpdateSet = performAggregation(updateInsts);
		
		runtime.getScheduler().getUpdateSet().clear();
		runtime.getScheduler().getUpdateSet().addAll(tempUpdateSet);
		
        runtime.getScheduler().getUpdateInstructions().clear();
}

	public UpdateList compose(UpdateList updateSet1, UpdateList updateSet2) {
		CompositionAPIImp compAPI = new CompositionAPIImp();
		compAPI.setUpdateInstructions(updateSet1, updateSet2);
		
		for(UpdateAggregator p : runtime.getAggregators()){
			p.compose(compAPI);
		}
		
		return compAPI.getComposedUpdates();
	}
	
	public UpdateList performAggregation(UpdateList updateInsts) {

		// instantiate engine aggregation API, and set update multiset
		AggregationHelperImpl aggAPI = new AggregationHelperImpl();
		aggAPI.setUpdateInstructions(updateInsts);
		
		// for each plugin
		for (UpdateAggregator p: runtime.getAggregators()) 
			p.aggregateUpdates(aggAPI);
		
		if (aggAPI.isConsistent() == false) {
			String msg = "Inconsistent aggregated results.";
			
			if (aggAPI.getFailedInstructions().size() > 0) {
				msg = msg + "\nFailed instructions: " + "\n" + aggAPI.getFailedInstructions();
			}
			if (aggAPI.getUnprocessedInstructions().size() > 0) {
				msg = msg + "\nUnprocessed instructions: " + "\n" + aggAPI.getUnprocessedInstructions();
			}
			throw new EngineError(msg);
		}
		
		// get resultant updates from agg API
		return aggAPI.getResultantUpdates();
	}
	
	public synchronized boolean isConsistent(UpdateList updateSet) {
		boolean isRegularUpdateSet = true;
		UpdateList uSet = updateSet;
		lastInconsistentUpdates = null;
		
		for (Update u: uSet) 
			if (!u.action.equals(Update.UPDATE_ACTION))
				isRegularUpdateSet = false;
		if (!isRegularUpdateSet) {
			if (uSet instanceof UpdateList) {
				uSet = performAggregation(uSet);
			} else
				throw new EngineError("Consistency check expects an update multiset.");
		}                
        HashMap updateMap = new HashMap();
		for (Update u: uSet) {
			if (updateMap.containsKey(u.loc)) {
				lastInconsistentUpdates = new UpdateList();
				lastInconsistentUpdates.add(u);
				lastInconsistentUpdates.add(updateMap.get(u.loc));
				return false;
			}
			else
				updateMap.put(u.loc, u);
		}
		return true;
	}

	public Element getNewElement() {
		return new Element();
	}

	/*
	 * See TR-2006-09, page 33.
	 *
	public Element getNewElementFrom(AbstractUniverse bkg) {
		if (bkg instanceof BackgroundElement) {
			return ((BackgroundElement)bkg).getNewValue();
		} else 
			if (bkg instanceof UniverseElement) {
				Element a = getNewElement();
				synchronized (this) {((UniverseElement)bkg).member(a, true);}
				return a;
			}
			else {
				return null;
			}
	}
	*/

	/**
	 * Pushes the current state in the stack.
	 */
	public void pushState() {
		Stack> updateStack = getUpdateStack();
		setStateStackedFlag(true);
		Map updates = new HashMap();
		updateStack.push(updates);
	}

	/**
	 * Retrieves the state from the top of the stack 
	 * (thus discarding the current state). 
	 */
	public void popState() {
		Stack> updateStack = getUpdateStack();

		if (updateStack.size() > 0)
			updateStack.pop();
		if (updateStack.size() == 0)
			setStateStackedFlag(false);
	}

	/**
	 * Applies the updates in the given update set to the current state.
	 * This method should only be called when there is a state in the stack.
	 * 
	 * @param updates the update multiset
	 * @see #pushState()
	 */
	public synchronized void apply(UpdateList updates) {
		if (isStateStacked()) {
			Stack> updateStack = getUpdateStack();

			// adding updates to the current update set in the stack
			// this will overwrite updates to the same location
			Map lastUpdates = updateStack.peek();
			for (Update u: updates)
				lastUpdates.put(u.loc, u.value);
			
		} else
			runtime.error("Cannot apply updates when state stack is empty.");
	}

	public Map getUniverses() {
		return state.getUniverses();
	}

	public AbstractUniverse getUniverse(String name) {
		return state.getUniverse(name);
	}
	
	public synchronized void addUniverse(String name, AbstractUniverse universe) throws NameConflictException {
		state.addUniverse(name, universe);
	}

	public Map getFunctions() {
		return state.getFunctions();
	}

	public FunctionElement getFunction(String name) {
		return state.getFunction(name);
	}

	public synchronized void addFunction(String name, FunctionElement function) throws NameConflictException {
		state.addFunction(name, function);
	}

	public Set getLocations() {
		return state.getLocations();
	}

	/**
	 * Return true if the given name is the name
	 * of a function in the state.
	 */
	public boolean isFunctionName(String token) {
		return getFunction(token) != null;
	}

	/**
	 * Return true if the given name is the name
	 * of a function in the state.
	 */
	public boolean isUniverseName(String token) {
		return getUniverse(token) != null;
	}

	public synchronized void clearState() {
		state = new HashState();
		/*
		 * The following universe and functions are moved to Kernel
		try {
			state.addFunction(SELF_FUNCTION_NAME, new MapFunction(Element.UNDEF));
			state.addFunction(PROGRAM_FUNCTION_NAME, new MapFunction(Element.UNDEF));
			state.addUniverse(AGENTS_UNIVERSE_NAME, new UniverseElement());
		} catch (NameConflictException e) {
			// This should not happen!
			throw new EngineError(e);
		}
		/**/
	}

    public String getFunctionName(FunctionElement function) {
		return state.getFunctionName(function);
	}

	public String toString() {
		return state.toString();
	}

	public UpdateList getLastInconsistentUpdate() {
		return lastInconsistentUpdates;
	}

	public FunctionElement getFunctionElementFunction() {
		return state.getFunctionElementFunction();
	}

	public FunctionElement getUniverseElementFunction() {
		return state.getUniverseElementFunction();
	}
	
	/**
	 * This class extends the {@link MapFunction} class and 
	 * provides a class of functions to keep named elements
	 * in the state.
	 * 
	 * @author Roozbeh Farahbod, 15-Sep-2006
	 */
	protected class NameTableFunction  extends FunctionElement {
		
		private Maptable;
		
		public NameTableFunction() {
			table = new HashMap();
		}
		
		public void setValue(String name, E value) {
			table.put(name, value);
		}
		
        @SuppressWarnings("unchecked")
		public void setValue(List args, Element value) throws UnmodifiableFunctionException {
            if (args.size() == 1){
                try {
                	setValue(args.get(0).toString(),(E) value);
                }
                catch (ClassCastException e) {
                    runtime.error(e);
                }
            }
            else {
            	runtime.error("NameTableFunctions can have only one argument.");
            }
        }
        
		public E getValue(String name) {
			return table.get(name);
		}
		
		public Collection values() {
			return (Collection)table.values();
		}

		public Collection getNames() {
			return table.keySet();
		}
		
		@Override
		public Element getValue(List args) {
			if (args.size() == 1)
				return this.getValue(args.get(0).toString());
			else
				return Element.UNDEF;
		}

		public Map getTable() {
			return table;
		}
		
		public Map getTableClone() {
			Map result = new HashMap();
			for (Entry e: table.entrySet()) {
				result.put(e.getKey(), e.getValue());
			}
			return result;
		}
		
		public boolean containsName(String name) {
			return table.containsKey(name);
		}
        
        public Set getLocations(String name) {
            Set locations = new HashSet();
            
            for (String functionName: table.keySet()) {
                locations.add(new Location(name, ElementList.create(new NameElement(functionName))));
            }
            
            return locations;
        }

	}
	
	/** 
	 *	An implementation of State using HashMap.
	 *   
	 *  @author  Roozbeh Farahbod
	 *  
	 *  @see java.util.HashMap
	 */
	protected class HashState implements State {

		public final long id;
		
		/**
		 * Universes
		 */
		private NameTableFunction universeElements;
		
		/**
		 * Functions
		 */
		private NameTableFunction functionElements;
		
		/**
		 * Creates a new HashState.
		 */
		public HashState() {
			super();
			lastStateId++;
			id = lastStateId;
			universeElements = new NameTableFunction();
			functionElements = new NameTableFunction();
			functionElements.setValue(UNIVERSE_ELEMENT_FUNCTION_NAME, universeElements);
			functionElements.setValue(FUNCTION_ELEMENT_FUNCTION_NAME, functionElements);
		}

		public Map getUniverses() {
			return universeElements.getTableClone();
		}

		public synchronized void addUniverse(String name, AbstractUniverse universe) throws NameConflictException {
			if (universe == null)
				throw new NullPointerException();
			if (nameExists(name))
				throw new NameConflictException("Identifier \"" + name + "\" is defined more than once.");
			universeElements.setValue(name, universe);
		}

		public Map getFunctions() {
			return functionElements.getTableClone();
		}

		public synchronized void addFunction(String name, FunctionElement function) throws NameConflictException {
			if (function instanceof AbstractUniverse)
				addUniverse(name, (AbstractUniverse)function);
			if (function == null)
				throw new NullPointerException();
			if (nameExists(name))
				throw new NameConflictException("Identifier \"" + name + "\" is defined more than once.");
			functionElements.setValue(name, function);
		}

		public Set getLocations() {
			HashSet locations = new HashSet();
			Map funcs = getFunctions();
			for (Entry e: funcs.entrySet()) 
				if (e.getValue().isModifiable()) 
					locations.addAll(e.getValue().getLocations(e.getKey()));
			return locations;
		}

		public Element getValue(Location loc) throws InvalidLocationException {
			if (nameExists(loc.name)) {
				Element id;
				try {
					id = getIdentifier(loc.name);
				} catch (IdentifierNotFoundException e) {
					throw new InvalidLocationException(e);
				}
				if (id instanceof FunctionElement) {
					FunctionElement f = (FunctionElement)id;
					if (f.isReadable()) 
						return ((FunctionElement)id).getValue(loc.args);
					else {
						String msg = "Reading from an out-function '" + loc + "' results in an undef value.";
//						CHANGE: Why print the same warning twice?
//						logger.warn(msg);
						runtime.warning("Abstract Storage", msg);
						return Element.UNDEF;
					}
				}
				else
					throw new InvalidLocationException(loc + " is not a valid location.");
			}
			else
				return Element.UNDEF;
		}
		
		/**
		 * Sets a new value for a location in the state.
		 * If the location does not exist, it adds a new location using
		 * a {@link MapFunction} instance to the state and then sets its value.
		 * 
		 * @param l location 
		 * @param v value to be set for the given location
		 * 
		 * @throws InvalidLocationException if the location is not modifiable.
		 * @see State#setValue(Location, Element)
		 */
		public synchronized void setValue(Location l, Element v) throws InvalidLocationException {
			if (!nameExists(l.name)) {
		        FunctionElement f = new MapFunction(Element.UNDEF);
	            try {
					addFunction(l.name, f);
				} catch (NameConflictException e) {
		            throw new EngineError("There is a name conflict (in 'handleUndefinedIdentifier(String, ElementList)') for \"" + id + "\"."); 
				} 
			}
			Element id;
			try {
				id = getIdentifier(l.name);
			} catch (IdentifierNotFoundException e) {
				throw new InvalidLocationException(e);
			}
			if (id instanceof FunctionElement) {
				if (((FunctionElement)id).isModifiable()) {
					try {
						((FunctionElement) id).setValue(l.args, v);
					} catch (UnmodifiableFunctionException e) {
						throw new InvalidLocationException(e);
					}
				} else
					throw new InvalidLocationException(l + " is not a modifiable location.");
			} else
				throw new InvalidLocationException(l + " is not a valid location.");
		}

		/*
		public synchronized void setValue(Location l, Element v) throws InvalidLocationException {
			if (nameExists(l.name)) {
				Element id;
				try {
					id = getIdentifier(l.name);
				} catch (IdentifierNotFoundException e) {
					throw new InvalidLocationException(e);
				}
				if (id instanceof FunctionElement && ((FunctionElement)id).isModifiable())
					try {
						((FunctionElement) id).setValue(l.args, v);
					} catch (UnmodifiableFunctionException e) {
						throw new InvalidLocationException(e);
					}
				else
					throw new InvalidLocationException("Not a valid location.");
			} else {
		        FunctionElement f = new MapFunction(Element.UNDEF);
	            addFunction(id, f); 
				throw new InvalidLocationException("There is no such function in the state.");
			}
		}
		*/

		/**
		 * Returns the rule/function/universe in the state
		 * that has the given name.
		 *  
		 * @param name
		 * @return an Element
		 * @throws IdentifierNotFoundException if there 
		 * is no such rule/function/universe in the state
		 */
		public Element getIdentifier(String name)
				throws IdentifierNotFoundException {
			Element id = null;
			id = (Element)universeElements.getValue(name);
			if (id == null) {
				id = functionElements.getValue(name);
				if (id == null) {
					//id = ruleElements.getValue(name);
					//if (id == null)
						throw new IdentifierNotFoundException();
				}
			}
			return id;
		}

		/*
		 * Returns true if a rule/function/universe with
		 * the given name exists in the state.
		 */
		private boolean nameExists(String name) {
			return universeElements.containsName(name)
					|| functionElements.containsName(name);
					//|| ruleElements.containsName(name);
		}

		public FunctionElement getFunction(String name) {
			FunctionElement res = functionElements.getValue(name);
			if (res == null) 
				res = universeElements.getValue(name);
			return res;
		}

		public AbstractUniverse getUniverse(String name) {
			return universeElements.getValue(name);
		}

		public String toString() {
			StringWriter strWriter = new StringWriter();
			PrintWriter writer = new PrintWriter(strWriter);
			String tempStr = null;
			
//			writer.println("State #" + this.id);
			
			Set> universeEntries = 
					universeElements.getTable().entrySet();
			
			writer.println("  * Backgrounds:");
			for (Entry e: universeEntries) {
				if (e.getValue() instanceof BackgroundElement) {
					writer.println("    - " + e.getKey());
				}
			}

			writer.println("  * Universes:");
			for (Entry e: universeEntries) {
				if (e.getValue() instanceof UniverseElement) {
					UniverseElement ue = (UniverseElement)e.getValue();
					writer.print("    - " + e.getKey() + ": {");
					StringBuffer str = new StringBuffer();
					for (Location l: ue.getLocations(e.getKey())) {
						if (ue.getValue(l.args).equals(BooleanElement.TRUE)) {
							if (l.args.size() > 0) {
								str.append(l.args.get(0).denotation() + ", ");
							}
						}
					}
					if (str.length() > 2) {
						writer.print(str.substring(0, str.length() - 2));
					}
					writer.println("}");
				}
			}

			writer.println("  * Functions:");
			for (Entry e: functionElements.getTable().entrySet()) {
				FunctionElement f = e.getValue();
				if (f.isModifiable() && 
						!(f.equals(functionElements) || f.equals(universeElements))) {
					String name = e.getKey();
					writer.println("    - " + name);
					for (Location l: f.getLocations(name)) {
						writer.print("      " + name + "(");
						tempStr = "";
						for (Element arg: l.args)
							tempStr = tempStr + arg.denotation() + ", ";
						if (tempStr.length() > 0)
							tempStr = tempStr.substring(0, tempStr.length() - 2);
						writer.print(tempStr);
						writer.print(") = ");
						writer.println(reformatFunctionValue(f.getValue(l.args).denotation()));
					}
				}
			}
			
			return strWriter.toString();
		}

		/* (non-Javadoc)
         * @see org.coreasm.engine.absstorage.State#getFunctionName(org.coreasm.engine.absstorage.FunctionElement)
         */
        public String getFunctionName(FunctionElement function) {
            for (String name: functionElements.table.keySet()) {
                if (functionElements.table.get(name).equals(function)) {
                    return name;
                }
            }

            for (Entry u : universeElements.table.entrySet()) {
            	if (u.getValue().equals(function))
            		return u.getKey();
            }
            return null;
        }

		/*
		 * Cut the string to a specific length
		 */
		private String reformatFunctionValue(String value) {
			StringBuffer result = new StringBuffer(value);
			int TRIM = 50;

			if (result.length() > TRIM) {
				result.delete(TRIM - 3, result.length());
				result.append("...");
			}
			
			return result.toString();
		}
		
		public FunctionElement getFunctionElementFunction() {
			return functionElements;
		}

		public FunctionElement getUniverseElementFunction() {
			return universeElements;
		}

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy