protected String findString(Modifiable E, List<String> ignoreStats, String defPrefix, String tagName, XMLTag piece, Map<String,Object> defined) throws CMException,PostProcessException
	{
		tagName=tagName.toUpperCase().trim();
		
		if(tagName.startsWith("SYSTEM_RANDOM_NAME:"))
		{
			String[] split=tagName.substring(19).split("-");
			if((split.length==2)&&(CMath.isInteger(split[0]))&&(CMath.isInteger(split[1])))
				return CMLib.login().generateRandomName(CMath.s_int(split[0]), CMath.s_int(split[1]));
			throw new CMException("Bad random name range in '"+tagName+"' on piece '"+piece.tag()+"', Data: "+CMParms.toKeyValueSlashListString(piece.parms())+":"+CMStrings.limit(piece.value(),100));
		}
		else
		if(tagName.equals("ROOM_AREAGATE"))
		{
			if(E instanceof Environmental)
			{
				final Room R=CMLib.map().roomLocation((Environmental)E);
				if(R!=null)
				{
					boolean foundOne=false;
					for(int d=0;d<Directions.NUM_DIRECTIONS();d++)
					{
						final Room R2=R.getRoomInDir(d);
						foundOne=(R2!=null) || foundOne;
						if((R2!=null) && (R2.roomID().length()>0) && (R.getArea()!=R2.getArea()))
							return CMLib.directions().getDirectionName(d);
					}
					if(!foundOne)
						throw new PostProcessException("No exits at all on on object "+R.roomID()+" in variable '"+tagName+"'");
				}
			}
			return "";
		}
		
		final String asParm = piece.getParmValue(tagName);
		if(asParm != null)
			return strFilter(E,ignoreStats,defPrefix,asParm,piece, defined);

		final Object asDefined = defined.get(tagName);
		if(asDefined instanceof String)
			return (String)asDefined;
		XMLTag processDefined=null;
		if(asDefined instanceof XMLTag)
		{
			piece=(XMLTag)asDefined;
			processDefined=piece;
			tagName=piece.tag();
		}
		List<XMLLibrary.XMLTag> choices = getAllChoices(E,ignoreStats,defPrefix,tagName, piece, defined,true);
		if((choices==null)||(choices.size()==0))
			throw new CMException("Unable to find tag '"+tagName+"' on piece '"+piece.tag()+"', Data: "+CMParms.toKeyValueSlashListString(piece.parms())+":"+CMStrings.limit(piece.value(),100));
		StringBuffer finalValue = new StringBuffer("");

		for(int c=0;c<choices.size();c++)
		{
			final XMLTag valPiece = choices.get(c);
			if(valPiece.parms().containsKey("VALIDATE") && !testCondition(E,null,null,CMLib.xml().restoreAngleBrackets(valPiece.getParmValue("VALIDATE")),valPiece, defined))
				continue;

			final String value=strFilter(E,ignoreStats,defPrefix,valPiece.value(),valPiece, defined);
			if(processDefined!=valPiece)
				defineReward(E,ignoreStats,defPrefix,valPiece.getParmValue("DEFINE"),valPiece,value,defined,true);

			String action = valPiece.getParmValue("ACTION");
			if(action==null) 
				finalValue.append(" ").append(value);
			else
			{
				action=action.toUpperCase().trim();
				if((action.length()==0)||(action.equals("APPEND")))
					finalValue.append(" ").append(value);
				else
				if(action.equals("REPLACE"))
					finalValue = new StringBuffer(value);
				else
				if(action.equals("PREPEND"))
					finalValue.insert(0,' ').insert(0,value);
				else
					throw new CMException("Unknown action '"+action+" on subPiece "+valPiece.tag()+" on piece '"+piece.tag()+"', Data: "+CMParms.toKeyValueSlashListString(piece.parms())+":"+CMStrings.limit(piece.value(),100));
			}
		}
		final String finalFinalValue=finalValue.toString().trim();
		if(processDefined!=null)
			defineReward(E,ignoreStats,defPrefix,piece.getParmValue("DEFINE"),processDefined,finalFinalValue,defined,true);
		return finalFinalValue;
	}