/*
author: mkraegeloh@ptc.com, now mkraegeloh@sulis.de

purpose: expose windchill functionality to command line.

This program is free software; you can redistribute it and/or modify it under the terms of the
GNU Lesser General Public License as published by the Free Software Foundation;
see http://www.gnu.org/licenses/lgpl.html (und deutsch: http://www.gnu.de/lgpl-ger.html)

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.

use -d with extreme care, ESPECIALLY ON PRODUCTION DATA. YOU CAN TOTALLY SCREW YOUR SYSTEM.
to avoid accidential damage delete operations are only performed if the property
ext.tools.ListAll.delete is set to true. (in wt.properties)

*/
package ext.tools;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.regex.*;
import wt.fc.*;
import wt.enterprise.*;
import wt.inf.container.*;
import wt.type.*;
import wt.session.*;
import wt.workflow.engine.*;
import wt.method.*;
import wt.vc.*;
import wt.vc.wip.*;
import wt.query.*;
import wt.util.*;
import wt.pom.*;
import wt.part.*;
import wt.occurrence.*;
import wt.eff.*;
import wt.iba.value.service.*;              //StandardIBAValueService
import wt.iba.value.*;                      //IBAHolder
import wt.iba.value.litevalue.*;            //AbstractValueView
import wt.iba.definition.*;                 //AttributeOrganizer
import wt.iba.definition.litedefinition.*;  //AttributeDefDefaultView
import wt.iba.definition.service.*;         //IBADefinitionDBService
import wt.iba.constraint.*;                 //IBAConstraintException
import com.ptc.core.meta.server.impl.*;

// 20031210 mkr; add history :-)
// 20031210 mkr; 1.34 sort methods first.
// 20040114 mkr; 1.36 search via -s using persistence layer
// 20040119 mkr; 1.37 sort methods in suachmas too.
// 20040128 mkr; 1.38 support brute force remove
// 20040129 mkr; 1.39 add -e -j flags
// 20040204 mkr; 1.40 add ability to list attached links
// 20040204 mkr; 1.41 weed duplicately retrieved links, get FIELDS from superclasses too
// 20040204 mkr; 1.42 try constant getRoleAObjectRef -> ROLE_A_OBJECT_REF and ROLE_AOBJECT_REF
// 20040204 mkr; 1.43 add getDisplayIdentity() to linked obj.
// 20040302 mkr; 1.44 filter list of displayed classes to peristables
// 20040313 mkr; 1.45 display ibas & type
// 20040330 mkr; 1.46 fix display of other side obj
// 20040407 mkr; 1.47 allow %xx escapes in oid (if copy/paste from browser url)
// 20040419 mkr; 1.48 display checkout info
// 20040423 mkr; 1.49 display eff info
// 20040427 mkr; 1.50 accept ufid
// 20040427 mkr; 1.51 fixEscapes locally (undone 20051103 - need prettyprint anyway.)
// 20040720 mkr; 1.52 skip calls to getUfid, dump vectors, populate occ data
// 20040804 mkr; 1.53 switch display from versionidentifier to versiondisplayidentifier
// 20040805 mkr; 1.54 fix assumption that toString() delivers correctly class:id
// 20040805 mkr; 1.55 for -o print DisplayIdentifier first, ,suppress found OK messages unless verbose, -F for free query in ms
// 20040805 mkr; 1.56 fix assignment of const_str in if(verbose)
// 20040805 mkr; 1.57 eventually fix search for class
// 20040805 mkr; 1.58 skip exceptions during -od
// 20041112 mkr; 1.59 add -rb (rollback) option
// 20041118 mkr; 1.60 print obid when ignoring exceptions
// 20041118 mkr; 1.61 prettyprint arrays
// 20050111 mkr; 1.62 print type of a version if master
// 20050214 mkr; 1.63 fixed eff display
// 20050308 mkr; 1.64 add more eff data
// 20050308 mkr; 1.65 print displayid on -od, fix email address
// 20050308 mkr; 1.66 add -x to dump serialized obj to stdout
// 20050515 mkr; 1.67 add -t (terse) to output only OBIDs, -w to suppress working copies, vvmethod stuff
// 20050602 mkr; 1.68 deal with IBA type reference
// 20050627 mkr; 1.69 convert from gnu regexp to jdk, add +[igelt] flags, call printing of effs per invoke to suppress error msg.
//                    delete only if ext.tools.ListAll.delete=true
// 20050708 mkr; 1.70 ibas: show now getName -> getDisplayName()=value
// 20050714 mkr; 1.71 make -o -a - default, -a will omit inherited methods.
// 20050719 mkr; 1.72 call orefInfo instead of dumpobj for -v flag - so +l now works always. add -m to list methods.
// 20050913 mkr; 1.73 prettyPrint() arrays returned before match() from invoked method
// 20051027 mkr; 1.74 add rullup (wt8 doesn't let you delete old iterations)
// 20051117 mkr; 1.75 no fixescapes if -s to leave abc%123 intact for sql
// 20060131 mkr; 1.76 fix dup class names in classregistry (complain)
// 20060131 mkr; 1.77 print LogicalIdentifier if link to LogicalIdentifierMapEntry
// 20060424 mkr; 1.78 -m prints constants too
// 20060703 mkr; 1.79 always talk in english. dodel rethrows exception on err.
// 20060703 mkr; 1.80 add -NN flag for n latest objs, wtpartusagelink prints associated usesoccurrences.
// 20060803 mkr; 1.81 minor edit for method search diagnostics.
// 20061115 mkr; 1.82 fix dumpObj NPE on null obj
// 20061123 mkr; 1.83 -r now accepts -rN where N=level of recursion (default=1)
// 20061128 mkr; 1.84 fix snafu with \n
// 20070131 mkr; 1.85 print processes

/**
  * commandline invocation to query details about windchill objects, also lets you delete objects.
  *
  * @author mkraegeloh
  * @version 1.0
  */

public class ListAll implements RemoteAccess{

   public static final String CVSVersion = "$Header: /usr/local/cvsroot/SULIS/src/ext/tools/ListAll.java,v 1.14 2007/01/31 17:00:23 mkr Exp $";
   private static final String myVersion = "1.85";
   String indent="";
   boolean classverbose=false;
   boolean verbose=false;
   Hashtable objSeen=null;
   Hashtable classregistry = null;
   WTProperties wtproperties = null;
   RemoteMethodServer rms=null;
   boolean sortDown=false;
   private static ReferenceFactory RF=null;
   private static Pattern dashdigits;
   private boolean noAuth=false;
   private boolean rollback=false;
   private boolean rollup=false;
   private Method vvMethod=null;
   private String vvMethodName="number";
   private String listMethodName="";
   private boolean printAll=true;
   private boolean printIbas=false;
   private boolean printProcesses=false;
   private boolean printEffs=false;
   private boolean printGetters=false;
   private boolean printLinks=false;
   private boolean printPIBAs=false;
   private boolean printType=false;
   private boolean mayDelete=false;
   private int nest=1;
   private int nested=0;
   private static String nl=System.getProperty("line.separator");

   static{
	try{
	dashdigits=Pattern.compile("^-(\\d+)$");
	RF=new ReferenceFactory();
	}catch(Exception kommtned){ throw new RuntimeException(kommtned);}
   }


   public static void main(String[] args) throws Exception{
	if(args.length == 0 || args.length == 1 && args[0].startsWith("-")){
	   System.out.println("");
	   System.out.println("usage: ListAll [-NN] [+X] [-V] [-v] [-d] [-i] [-n] [-f] [-s] [-j] [-e] class.to.Search getMethodToUse reg-exp.to.match");
	   System.out.println("       -v=verbose, -d=delete, -i=ignorecase, -n=use no-match -s=use SearchCondition");
	   System.out.println("       -f=force delete via pom -j=sort newer objects downwards -e=suppress exception stack trace");
	   System.out.println("       -V=suppress iteration output -VV[method]=suppress previous versions too (per unique method (number) return value)");
	   System.out.println("       -t=terse->output only obids -NN=print NN newest objects of given class (=highest ida2a2)"+nl+"see below for +X flags");
	   System.out.println("   or: ListAll -x obid");
	   System.out.println("       will instantiate the obj, then dump it's serialization data to stdout");
	   System.out.println("   or: ListAll -rb latest-iteration-obid to-be-kept-iteration-obid");
	   System.out.println("       roolback from latest to iteration to be kept (read: delete newest iteration[s])");
	   System.out.println("   or: ListAll -ru lowest-iteration-to-be-deleted-obid to-be-kept-iteration-obid");
	   System.out.println("       rollup from lowest to iteration to be kept (read: delete old iterations)");
	   System.out.println("   or: ListAll -o [+X] [-a] [-rN] [OV]R:class:ID");
	   System.out.println("       dumps the result of all getter methods. To allow reliable parsing [\\r\\n]+ in strings is replaced by \"\\n\".");
	   System.out.println("       -a to exclude superclass/interface methods.");
	   System.out.println("       -r to follow returned objects to N levels depth, default=1.");
	   System.out.println("       see below for +X flags");
	   System.out.println("   or: ListAll -u [-a] [-rN] UFID");
	   System.out.println("       dumps the result of all getter methods.");
	   System.out.println("       -a to exclude superclass/interface methods. -r to follow returned objects to Nth level.");
	   System.out.println("   or: ListAll [-f] -od obid [obid ...]");
	   System.out.println("       delete given obids, -f=force delete via pom, must be first arg");
	   System.out.println("   or: ListAll class-pattern");
	   System.out.println("       list all matching classes from classRegistry, then prompts for class/method/pattern");
	   System.out.println("   or: ListAll -m [-a] java.class");
	   System.out.println("       print a list of all methods with signatures");
	   System.out.println("       -a to exclude superclass/interface methods.");
	   System.out.println("   or: ListAll -NN java.class");
	   System.out.println("       will print the obids of the newest NN objects, e.g., ListAll -5 wtpart");
	   System.out.println("");
	   System.out.println("class can also be given as (absolute :-) unix name with .java or .class extension");
	   System.out.println("method e.g., getBlah can also be given as [bB]lah - if not found first method containing");
	   System.out.println("the string is used. if it contains a colon like myatt:number it will use getMyAtt for");
	   System.out.println("selection, and getNumber for output.");
	   System.out.println("without using -s flag all objects are retrieved and their getter method is called and the result");
	   System.out.println("is regexp-matched against the pattern specified. on large databases this is slow, therefore");
	   System.out.println("the -s flag can be used to add a searchcondition on the queryspec. this works for persistables.");
	   System.out.println("the wt searchcondition runs with LIKE, which means you must supply wildcards as % and _");
	   System.out.println("");
	   System.out.println("WARN: use of this tool to delete data - especially with the -f flag - on production data");
	   System.out.println("must only be done after proper backups and works obly if property ext.tools.ListAll.delete is true!!");
	   System.out.println("try to avoid -f as it bypasses *all* event handling and cleanup of links etc.");
	   System.out.println("");
	   System.out.println("+X: during verbose output giving flags +[ieglrpt] will only print Ibas Effs Getters Links Related-process PathOccIBAs Type");
	   System.out.println("   (e.g., +i only outputs Ibas)");
	   System.out.println("   using +X will assume -o if first non-flag arg matches :[0-9]+, else -v");
	   System.out.println("");
	   System.out.println("as of 1.80 sorting has been changed back from read-all-into-array then sort array method to");
	   System.out.println("applying a order-by on ida2a2. BUT: because e.g., wtpart internally unions wtproducts table this will");
	   System.out.println("now print first all wtparts, then all wtproducts.");
	   System.out.println("!! if you use ListALl -5 wtpart you will likely miss newer wtproducts!!");
	   System.out.println("");
	   System.out.println("comments, enhancement requests and complaints to martl@sulis.de ;-)");
	   System.out.println("current Version is: "+myVersion);
	   System.out.println("");
	   System.exit(1);
	}
	char[] ca;
	for(int i=0;i<args.length;i++){
	   ca=args[i].toCharArray();
	   for(int j=0;j<ca.length;j++){
		if(ca[j] == 0xfffd){
		   System.out.println("ERROR: found \\xFFFD in args - assuming bad encoding settings.");
		   System.exit(1);
		}
	   }
	}
	WTContext.init(args);
	if(!WTContext.getContext().getLocale().getLanguage().equals("en")){
	   //System.out.println("INFO: switching language from "+WTContext.getContext().getLocale().getLanguage()+" to en");
	   WTContext.getContext().setLocale(Locale.ENGLISH);
	}
	System.exit((new ListAll()).process(args)); // let calling  prog know status ...
   }

   public ListAll(){
	try{
	mayDelete = WTProperties.getLocalProperties().getProperty("ext.tools.ListAll.delete","false").equalsIgnoreCase("true");
	}catch(Exception ignore){} // false is always secure.
	objSeen=new Hashtable();
   }

   public ListAll(boolean verbose){
	this();
	this.classverbose=verbose;
   }

   public int process(String[] a) throws Exception{

	int off=0;
	int maxLoop=1;
	boolean notMatch=false;
	boolean delete=false;
	boolean useMethod=false;
	String oref=null;
	String ufid=null;
	boolean all=true;
	boolean useSearchCond=false;
	boolean haveOid=false;
	boolean haveUfid=false;
	boolean force=false;
	boolean noInfo=false;
	boolean serializeIt=false;
	boolean noWork=false;
	boolean terse=false;
	boolean listMethods=false;
	int VROnly=0;
	int ignore=0;
	int i=0;
	int manywant=0;
	boolean recurse=false;

	Matcher mdigits;
	for(i=0;i<a.length && (a[i].startsWith("-")||a[i].startsWith("+")); i++){
	   mdigits=dashdigits.matcher(a[i]); // to find -NN
	   if(a[i].equals("-i")){ ignore=Pattern.CASE_INSENSITIVE; off++;}
	   else if(a[i].equals("-n")){ notMatch=true; off++;}
	   else if(a[i].equals("-x")){ serializeIt=true; off++;}
	   else if(a[i].equals("-s")){ useSearchCond=true; off++;}
	   else if(a[i].equals("-v")){ verbose=true; off++;}
	   else if(a[i].equals("-V")){ VROnly++; off++;}
	   else if(a[i].equals("-d")){ maxLoop=5; delete=true; off++;}
	   else if(a[i].equals("-o")){ haveOid=true; off++; }
	   else if(a[i].equals("-u")){ haveUfid=true; off++; }
	   else if(a[i].equals("-a")){ all=false; off++;}
	   else if(a[i].equals("-rb")){ rollback=true; off++;}
	   else if(a[i].equals("-ru")){ rollup=true; off++;}
	   else if(a[i].equals("-j")){ sortDown=true; off++;}
	   else if(a[i].equals("-e")){ noInfo=true; off++;}
	   else if(a[i].equals("-m")){ listMethods=true; off++;}
	   else if(a[i].equals("-t")){ terse=true; off++;}
	   else if(a[i].equals("-w")){ noWork=true; off++;}
	   else if(a[i].equals("-od") || a[i].equals("-do")){ off++; delete=true; haveOid=true; }
	   else if(a[i].startsWith("-r")){
	      recurse=true;
		off++;
		if(a[i].length()>2){
		   nest=Integer.parseInt(a[i].substring(2));
		}
	   }
	   else if(a[i].startsWith("-VV")){
	     VROnly=2;
	     off++;
	     if(a[i].length() > 3)
	        vvMethodName=a[i].substring(3);
	   }
	   else if(a[i].equals("-f")){
	      force=true; off++;
	      if(rms == null)rms=RemoteMethodServer.getDefault();
	   }
	   else if(a[i].equals("-F")){
	      noAuth=true; off++;
	      if(rms == null)rms=RemoteMethodServer.getDefault();
	   }
	   else if(a[i].startsWith("+")){
		printAll=false;
	      byte[] by=a[i].getBytes();
		for(int bi=1;bi<by.length;bi++)
		   if(by[bi] == 'i')
		      printIbas=true;
		   else if(by[bi] == 'r')
		      printProcesses=true;
		   else if(by[bi] == 'p')
		      printPIBAs=true;
		   else if(by[bi] == 't')
		      printType=true;
		   else if(by[bi] == 'e')
		      printEffs=true;
		   else if(by[bi] == 'g')
		      printGetters=true;
		   else if(by[bi] == 'l')
		      printLinks=true;
	      off++;
	   }
	   else if(mdigits.matches()){
	      manywant=Integer.parseInt(mdigits.group(1));
		off++;
	   }
	}
	// guess it's save to replace all %xx with char value ...
	// ... no, it's not. -s will use % ...
	if(! useSearchCond)
	   ToolUtils.fixEscapes(a);

	// can we establish reasonable default for + flags?
	if(! printAll && ! haveOid && ! verbose)
	   if(Pattern.compile(":[0-9]+").matcher(a[off]).find())
	      haveOid=true;
	   else
	      verbose=true;

	if(rollback) return rollback(a[off],a[off+1]);
	if(rollup) return rollup(a[off],a[off+1]);
	if(serializeIt) return serializeIt(a[off]);

	if(haveOid && delete) return delObjs(a,off,force);
	if(haveOid)
	   oref=a[off++];
	if(haveUfid)
	   ufid=a[off++];

	// special case: info about object ref
	if (oref != null) return orefInfo(oref,all, recurse);

	// special case: info about ufid
	if (ufid != null) return ufidInfo(ufid, all, recurse);

	if(manywant==0 && ! listMethods && a.length == off+1){ // search class & method, may end calling System.exit()
	   a=suachmas(a[off]);
	   off=0; // new args
	}

	// some magic on class to search
      String cl = null;
	Class c = null;
      Object obj = null;
      if(a[off].indexOf(".") == -1){
	   loadprops(); // classregistry
	   // case insentitive search
	   Enumeration e=classregistry.keys();
	   String nm,nu;
	   Hashtable upper=new Hashtable();
	   while(e.hasMoreElements()){
	      nu=(nm=(String)e.nextElement()).toUpperCase();
		upper.put(nu,classregistry.get(nm)+"."+nm);
	   }
	   cl = (String) upper.get(a[off].toUpperCase());
	   if(cl==null||cl.equals(""))
		 cl = a[off];
	   try{
		c = Class.forName(cl);
		if(verbose)System.out.println("class " + cl + " found OK");
	   }
	   catch(Exception x){
		System.out.println("class " + cl + " NOT found");
		return 1;
	   }
	}
	else{
	   String s2 = "";
	   cl = a[off].replaceFirst(".*codebase/", "");
	   cl = cl.replaceFirst("\\.(class|java)$", "");
	   cl = cl.replaceFirst("^/", "");
	   cl = cl.replaceAll("/", ".");
	   if(!cl.equals(a[off]))
		 s2 = a[off] + "->" + cl + '\n';
	   do
		 try
		 {
		     c = Class.forName(cl);
		     if(verbose)System.out.println("class " + cl + " found OK");
		     break;
		 }
		 catch(Exception exception)
		 {
		     s2 = (new StringBuffer(s2)).append("class ").append(cl).append(" NOT found"+nl).toString();
		     if(cl.indexOf(".") == -1)
		     {
			   System.out.println(s2);
			   return 1;
		     }
		     cl = cl.replaceFirst(".*?\\.", "");
		 }
	   while(true);
	}

	if(listMethods){
	   System.out.println("methods of "+c.getName()+":");
	   Method[] ma;
	   if(all)
		ma=c.getMethods();
	   else
		ma=c.getDeclaredMethods();
	   Arrays.sort(ma,new Comparator(){
	      public int compare(Object o1, Object o2){
		   int i=((Method)o1).getDeclaringClass().getName().compareTo(((Method)o2).getDeclaringClass().getName());
		   if (i!=0)return i;
		   return ((Method)o1).getName().compareTo(((Method)o2).getName());
		}
	   });
	   for(int mi=0;mi<ma.length;mi++){
		System.out.println("  " + ma[mi]);
		//Class[] c = ma[mi].getParameterTypes();
		//for(int j=0;j<c.length; j++)
		//   System.out.println("  arg["+j+"]=" + c[j]);
	   }
	   System.out.println("constants of "+c.getName()+":");
	   Field[] fa=c.getFields();
	   Arrays.sort(fa, new Comparator(){
		public boolean equals(Object o1, Object o2){
		   return ((Field)o1).getName().equals(((Field)o2).getName());
		}
		public int compare(Object o1, Object o2){
		   return ((Field)o1).getName().compareTo(((Field)o2).getName());
		}
	   });
	   for(int fi=0;fi<fa.length;fi++)
	      System.out.println("  "+fa[fi].getName()+"="+fa[fi].get(c));
	   return 0;
	}
	Pattern r;
	String mn;
	if(manywant>0 && a.length<off+3){
	   r= Pattern.compile(".",ignore);
	   mn=".";
	   terse=true;
	}
	else{
	   r= Pattern.compile(a[off+2],ignore);
	   mn=a[off+1];
	}
	Matcher rm=null;
	Method m = null, mList=null;
	String const_str="";
	String const_str1="";
	if(! mn.equals(".")){
	   int co=mn.indexOf(":");
	   if(co != -1){ // different display than search
		String m1=mn.substring(0,co);
		String m2=mn.substring(co+1);
		if((m=findMethod(m1,c))==null){
		   System.out.println("class "+a[off+0]+" has no "+m1+"() method to SEARCH:list!");
		   return 1;
		}
		const_str=m.getName();
		useMethod=true;
		if((mList=findMethod(m2,c))==null){
		   System.out.println("class "+a[off+0]+" has no "+m2+"() method to search:LIST!");
		   return 1;
		}
	   }
	   else{
	      if((m=findMethod(mn,c)) == null){
		   System.out.println("class "+a[off+0]+" has no "+mn+"() method to search!");
		   return 1;
		}
		else{
		   mList=m;
		   const_str=m.getName();
		   if(verbose)System.out.println("method " + const_str + "() found OK");
		   useMethod=true;
		}
	   }
	}
	// same for vvmethod
	if(VROnly > 1){
	   vvMethod=findMethod(vvMethodName,c);
	   if(vvMethod == null){
		System.out.println("class "+a[off+0]+" has no "+vvMethodName+"() method to filter VV (latest version only) on!");
		return 1;
	   }
	}
	int many,bad;
	int redo=0;
	Object t;
	boolean nothing=false;
	do{
	   bad=many=0;
	   QuerySpec qs=new QuerySpec (c);
	   qs.appendOrderBy(c, "thePersistInfo.theObjectIdentifier.id", !sortDown);
	   if(useSearchCond){
		// prep const string ...
		const_str1 = const_str.replaceAll("([A-Z]+)", "_$1");
		const_str = const_str.substring(3).replaceAll("([A-Z])", "_$1");
		const_str1 = const_str1.replaceFirst("^_", "").toUpperCase();
		const_str = const_str.replaceFirst("^_", "").toUpperCase();
		Field f=null;
		try{
		   f=c.getField(const_str);
		}catch(Exception notfound){;} // try const_str1
		if(f==null)
		   f=c.getField(const_str1);
		//System.out.println("c="+const_str+", c1="+const_str1+" f="+f);
		//System.out.println("where="+f.get(c).toString()+" "+ SearchCondition.LIKE+" '"+a[off+2]+"'");
		if(notMatch)
		   qs.appendWhere(new SearchCondition(c, f.get(c).toString(), SearchCondition.NOT_LIKE, a[off+2]));
		else
		   qs.appendWhere(new SearchCondition(c, f.get(c).toString(), SearchCondition.LIKE, a[off+2]));
	   }
	   QueryResult qr = serverQuery(qs);

	   String n,t1,t2="";
         Hashtable VRSeen=new Hashtable();
	   StringBuffer line=new StringBuffer();
	   boolean hit=false;
	   int manydone=0;
	   while(qr.hasMoreElements()){
		if(manywant>0&&manydone++==manywant)
		    break;
		n="NO_METHOD_GIVEN";
		line.setLength(0);
		t=qr.nextElement();
		try{
		   if(useMethod){
		      Object on=m.invoke(t,new Object[]{});
			if(on instanceof Object[])
			   n=ToolUtils.prettyPrint(on).replaceAll("[\\x0a\\x0d]+"," ");
			else
			   n=""+on;
			if(! useSearchCond && ! (hit=(rm=r.matcher(n)).find()) && ! notMatch || notMatch && hit){
			   continue;
			}
			if(! m.equals(mList))
			   n=""+mList.invoke(t,new Object[]{});
		   }
		   if(delete){
			doDel(new Object[]{t}, force);
			nothing=false;
			many++;
		   }
		   else{
			if(noWork && WorkInProgressHelper.isWorkingCopy((Workable)t)) continue;
			line.append(""+n+" (");
			t1=RF.getReferenceString((Persistable)t);
			if(VROnly > 0 && ! t1.startsWith("VR")) continue; // no iterations.
			if(t1.startsWith("VR")){
			   if(VROnly > 1 && VRSeen.get(t2=vvMethod.invoke(t,new Object[]{}).toString())!=null) continue;
			   VRSeen.put(t2,"");
			   line.append("OR:"+t.getClass().getName()+":"+PersistenceHelper.getObjectIdentifier((Persistable)t).getId()+"/");
			}
			line.append(t1);
			String delim="/";
			if(t instanceof RevisionControlled){
			   line.append(delim+((RevisionControlled)t).getVersionDisplayIdentifier());
			   delim=".";
			}
			if(t instanceof Iterated) line.append(delim+((Iterated)t).getIterationIdentifier().getValue());
			if(t instanceof Workable && WorkInProgressHelper.isCheckedOut((Workable)t))
			   line.append("/"+(WorkInProgressHelper.isWorkingCopy((Workable)t) ? "wrk":"c/o"));
			if(! terse)
			   System.out.println(line.append(")").toString());
			else
			   System.out.println("OR:"+t.getClass().getName()+":"+PersistenceHelper.getObjectIdentifier((Persistable)t).getId());
			if(verbose){
			   System.out.println("details:");
			   //dumpObj(t,true,false);
			   orefInfo("OR:"+t.getClass().getName()+":"+PersistenceHelper.getObjectIdentifier((Persistable)t).getId(),true,false);
			}
		   }
		}
		catch(Exception e){
		   if(! noInfo)
			System.out.println("ON "+t+" ignore "+e);
		   bad++;
		}
	   }
	   if(delete){
	      System.out.println("deleted " + many + " object" + (many == 1 ? "" : "s"));
		if(nothing && many==0)
		   break; // 2nd time nothing deleted.
		nothing=many==0;
	   }
	   if(many == 0 || bad > 0) redo++;
	}while(redo < maxLoop && ! (many==0&&bad==0));

	return 0;
   }

   private int ufidInfo(String ufid, boolean all, boolean recurse) throws Exception{
	   return orefInfo(wt.adapter.BasicWebjectDelegate.getObjectByUfid(ufid).toString(),all,recurse);
   }
   private int orefInfo(String oref, boolean all, boolean recurse){
	Object o=null;
	try{
	   WTReference wtRef=RF.getReference(oref);
	   o=wtRef.getObject();
	}
	catch(Exception igitt){
	   igitt.printStackTrace();
	   return 1;
	}
	if(o instanceof WTObject)
	   System.out.print(((WTObject)o).getDisplayIdentifier());
	if(o instanceof Iterated)
	   System.out.print("  Iteration "+((Iterated)o).getIterationIdentifier().getValue()+ " latest="+VersionControlHelper.isLatestIteration((Iterated)o));
	System.out.println("");
	if(o instanceof Persistable && ( printAll || printLinks)){ try{
	   if(rms == null)
	      rms=RemoteMethodServer.getDefault();
	   QueryResult queryresult = (QueryResult)
	   rms.invoke("doServerExpand","ext.tools.ListAll",null, new Class[] {wt.fc.Persistable.class}, new Object[] { (Persistable) o });
	   Link link = null;
	   Hashtable unique=new Hashtable();
	   while(queryresult.hasMoreElements()){
		link = (Link)queryresult.nextElement();
		unique.put(link.toString(),link);
	   }
	   Iterator it=unique.values().iterator();
	   Object ol=null;
	   boolean needHead=true;
	   StringBuffer line=new StringBuffer();
	   while(it.hasNext()){
		link=(Link)it.next();
		if(needHead)
		   line.append("Links:"+nl);
		line.append("   "+link);
		line.append(" --> "+(ol=link.getOtherObject((Persistable)o)));
		if(ol instanceof LogicalIdentifierMapEntry)
		   line.append(" ("+((LogicalIdentifierMapEntry)ol).getLogicalIdentifier()+")");
		else if(ol instanceof WTObject)
		   line.append(" ("+((WTObject)ol).getDisplayIdentity()+")");
		line.append(nl);
		needHead=false;
	   }
	   System.out.print(line.toString());
	}catch(Exception e){
	   e.printStackTrace();
	   System.out.println("exception " + e + " expanding links");
	}
	}
	return dumpObj(o, all, recurse);
   }
   private int delObjs(String[] a, int off, boolean force){
	Object o=null;
	Object[] oa=new Object[a.length-off];
	int oai=0;
	String oref="";
	try{
	   for(int i=off; i<a.length; i++){
		oref = a[i].replaceAll("(?i)%3a",":");
		WTReference wtRef=RF.getReference(oref);
		oa[oai++]=wtRef.getObject();
	   }
	   doDel(oa,force);
	}
	catch(Exception igitt){
	   igitt.printStackTrace();
	}
      return 0;
   }
   public int dumpObj(Object o, boolean all, boolean recurse){
	if(o==null){
	   System.out.println("NULL");
	   return 0;
	}
	System.out.println("");
	if(nest<nested)
	   return 0;
	nested++;

	if(classverbose)System.out.println("dumpObj: " + o + " ("+o.getClass().getName()+")");

	String s=null;
	try{
	   s=""+o;
	   if(o instanceof Persistable ) s=RF.getReferenceString((Persistable)o);
	   if(objSeen.get(s)!=null){
		System.out.println(indent+"   ...");
		nested--;
		return 0;
	   }
	}catch(Exception naSoWas){
	   naSoWas.printStackTrace();
	}
      if(o instanceof OccurrenceableLink){
         try{
         Vector p1=new Vector(1);
         p1.add(o);
         p1=OccurrenceHelper.service.getPopulatedOccurrenceableLinks(p1);
         o=p1.get(0);
         }
         catch(Exception sowas){
	      sowas.printStackTrace();
	   }
      }
	objSeen.put(s,"");
	String oldIndent=indent;
	indent += "   ";
	try{
	   if(printAll || printGetters){
		if(nested==1)
		   System.out.println(oldIndent+"getter method values:");
		Class class1 = o.getClass();
		Method[] m;
		if(all) m=class1.getMethods();
		else m=class1.getDeclaredMethods();
		Arrays.sort(m, new Comparator(){
		   public boolean equals(Object o1, Object o2){
			return ((Method)o1).getName().equals(((Method)o2).getName());
		   }
		   public int compare(Object o1, Object o2){
			return ((Method)o1).getName().compareTo(((Method)o2).getName());
		   }
		});
		Object[] a={};
		Object o1=null;
		String n=null;
		for(int i=0;i<m.length;i++){
		   // what to skip  ...
		   if(! ((n=m[i].getName()).startsWith("get") ||
			    n.startsWith("is")&&m[i].getParameterTypes().length ==0) ||    // no getter method
			n.endsWith("Default")                                        ||    // endless loop :-)
			n.equals("getClass")                                         ||    // getClass unnecessary
			! Modifier.isPublic(m[i].getModifiers())                           // only use public getters
			) {
			if(classverbose)System.out.println(indent+"skip: " + m[i]);
			continue;
		   }
		   System.out.print(indent+m[i].getName()+" -> ");
		   if(classverbose)System.out.print(indent+" "+m[i].getReturnType()+" "+m[i].getName()+" -> ");

		   if(m[i].getName().equals("getUfid"))
			o1="<skipped>";
		   else
		   try{
			o1=m[i].invoke(o,a);
		   }
		   catch(InvocationTargetException r){
		      Throwable t=r.getCause();
			o1="InvocationTargetException calling "+m[i]+" Cause="+t;
			if(verbose && t !=null) t.printStackTrace();
		   }
		   catch(Exception err){
			if(err instanceof java.lang.IllegalArgumentException)
			   o1=""+o;
			else
			   o1="err calling "+m[i]+" "+err;
		   }
		   if(o1 == null){
			System.out.println("<null>");
		   }
		   else if(o1 instanceof Object[]){
			System.out.println(ToolUtils.prettyPrint(o1).replaceAll("[\\x0a\\x0d]+"," "));
		   }
		   else if(o1 instanceof Vector){
			System.out.println("Vector: " + o1);
			if(recurse){
			   Vector v=(Vector)o1;
			   for(int k=0;k<v.size();k++)
				if(dumpObj(v.get(k), all, recurse) != 0){
				   indent=oldIndent;
				   nested--;
				   return 1;
				}
			}
		   }
		   else if(o1 instanceof String ||
			     o1 instanceof Long ||
			     o1 instanceof Boolean ||
			     ! recurse ||
			     (""+m[i].getReturnType()).startsWith("class java")){
			System.out.println((""+o1).replaceAll("[\\x0a\\x0d]+","\\\\n"));
		   }
		   else if(recurse){
			System.out.print("object: " + ToolUtils.prettyPrint(o1,false));
			if(dumpObj(o1, all, recurse) != 0){
			   indent=oldIndent;
			   nested--;
			   return 1;
			}
		   }
		}
	   }
	   if(o instanceof EffManagedVersion && (printAll || printEffs)){
	      System.out.print(getEffInfo((EffManagedVersion)o,oldIndent));
         }
	   if(o instanceof Typed && (printAll || printType))
		System.out.println(oldIndent+"Type:"+nl+"   "+oldIndent+TypedUtilityServiceHelper.service.getExternalTypeIdentifier((Typed)o));
	   if(o instanceof Master && (printAll || printType)){
	      QueryResult iters=VersionControlHelper.service.allVersionsOf((Mastered)o);
		if(iters.size() > 0){
		   Object wo=iters.nextElement();
		   if(wo instanceof Typed)
			System.out.println(oldIndent+"Master for Type:"+nl+"   "+oldIndent+TypedUtilityServiceHelper.service.getExternalTypeIdentifier((Typed)wo));
		}
	   }
	   if(o instanceof PartPathOccurrence && (printAll || printPIBAs)){
		PartPathOccurrence ppa=(PartPathOccurrence)o;
		Persistable poui;
	      QueryResult iters=VersionControlHelper.service.allVersionsOf((Mastered)ppa.getContext());
		System.out.print(oldIndent+"PathOccurrenceData:");
		System.out.println(getPathOccInfo((Persistable)o,oldIndent));
		if(iters.size() > 0){
		   Object wo=iters.nextElement();
		   System.out.println("   Context "+((WTObject)wo).getDisplayIdentity()+"."+((Iterated)wo).getIterationIdentifier().getValue());
		   // populate ... dump
		   QueryResult pod = OccurrenceHelper.service.getPathOccurrenceData(ppa, (PathOccurrenceContext)wo , PathOccurrenceData.class);
		   while(pod.hasMoreElements()){
			System.out.print(oldIndent+"      "+RF.getReferenceString(poui=(Persistable)pod.nextElement()));
			System.out.println(" master="+((PathOccurrenceUserIBAs)poui).getMasterReference());
			printIBAs(poui,oldIndent+"      ");
		   }
		}
	   }
	   if(o instanceof WTPartUsageLink && printAll){
		Vector uv=((WTPartUsageLink)o).getUsesOccurrenceVector();
		System.out.println("UsesOccurences:");
		if(uv==null)uv=new Vector();
		PartUsesOccurrence puo;
		String pr;
		for(int uvi=0;uvi<uv.size(); uvi++){
		   puo=(PartUsesOccurrence)uv.get(uvi);
		   pr="";
		   if(puo.getPathOccurrence()!=null)
		      pr=RF.getReferenceString(puo.getPathOccurrence());
		   System.out.println("   "+RF.getReferenceString(puo)+" name="+puo.getName()+" uoid="+puo.getUsesOccurrenceIdentifier()+" pocc="+pr);
		}
	   }

	   if(o instanceof IBAHolder && (printAll || printIbas)){
	      printIBAs(o,oldIndent);
	   } // ibaholder
	   if(o instanceof Persistable && (printAll || printProcesses)){
	      printProcesses((Persistable)o,oldIndent);
	   } // ibaholder
	}
	catch(Exception igitt){
	   igitt.printStackTrace();
	   indent=oldIndent;
	   nested--;
	   return 1;
	}
	indent=oldIndent;
	nested--;
      return 0;
   }

   public void loadprops() throws Exception{
	if(wtproperties == null){
	   wtproperties = WTProperties.getLocalProperties();
	   String cb = wtproperties.getProperty("wt.codebase.location");
	   /* mkr: bad, dup keys!
	   classregistry=new Properties();
	   classregistry.load(new FileInputStream(cb + File.separator + "classRegistry.properties"));
	   */
	   FileInputStream f = new FileInputStream(cb + File.separator + "classRegistry.properties");
	   BufferedReader b = new BufferedReader(new InputStreamReader(f));
	   classregistry=new Hashtable();
	   int off=0;
	   String key,s;
	   Vector v;
	   Object o;
	   while((s = b.readLine()) != null){
		if((off=s.indexOf("="))==-1)
		   continue;
	      key=s.substring(0,off);
		if((o=classregistry.get(key))!=null){ // clash
		   if(o instanceof String){ // 2nd
		      classregistry.put(key,v=new Vector());
			v.add(o);
		   }
		   else
		      v=(Vector)o;
		   v.add(s.substring(off+1));
		}
		else
		   classregistry.put(key,s.substring(off+1));
	   }
	}
   }

   public String[] suachmas(String pattern)throws Exception{
	loadprops();
	Enumeration e=classregistry.keys();
	String nm,nu;
	Hashtable upper=new Hashtable();
	Vector v=new Vector();
	Pattern r = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
	Matcher rm=null;
	String newArgs[]=new String[3];
	Object o;
	boolean warned=false;
	while(e.hasMoreElements()){
	   nm=(String)e.nextElement();
	   if(r.matcher(nm).find())
		try{
		   if((o=classregistry.get(nm)) instanceof Vector){ // not unique, complain
			System.out.println("Warn: "+nm+" is not unique: "+o);
			warned=true;
			continue;
		   }
		   if(Persistable.class.isAssignableFrom(Class.forName(classregistry.get(nm)+"."+nm)))
		   v.add(nm);
		}
		catch(Throwable noAdd){;} // class may not let itself instanitate, in which case we jsut skip it.
	}
	if(v.isEmpty()){
	   System.out.println("no match.");
	   System.exit(1);
	}

	BufferedReader bin=new BufferedReader(new InputStreamReader(System.in));

	int n=v.size();
	int ansi=0;
	String ans="";
	Object[] oa=v.toArray();
	if(n > 1){
	   Arrays.sort(oa, new Comparator(){
		public boolean equals(Object o1, Object o2){
		   return ((String)o1).equals((String)o2);
		}
		public int compare(Object o1, Object o2){
		   return ((String)o1).compareTo((String)o2);
		}
	   });
	   for(int i=0;i<n;i++)
		System.out.println(("       "+i).substring((""+i).length())+": "+oa[i]);
	   System.out.print("choose class --> ");
	   System.out.flush();
	   ans=bin.readLine();
	   if(ans == null || ans.equals("")) System.exit(1);
	   ansi=Integer.parseInt(ans);
	}
	else
	   System.out.println("using "+oa[0]);
	newArgs[0]=classregistry.get(oa[ansi])+"."+oa[ansi];
      Class c = Class.forName(newArgs[0]);
	Method[] ml=c.getMethods();
	Arrays.sort(ml, new Comparator(){
	   public boolean equals(Object o1, Object o2){
		return ((Method)o1).getName().equals(((Method)o2).getName());
	   }
	   public int compare(Object o1, Object o2){
		return ((Method)o1).getName().compareTo(((Method)o2).getName());
	   }
	});
	String mname=null;
	v.removeAllElements();
	for(int i=0;i<ml.length;i++)
	   if((mname=ml[i].getName()).startsWith("get"))
	      v.add(mname);
	n=v.size();
	ansi=0;
	if(n > 1){
	   for(int i=0;i<n;i++)
		System.out.println(("       "+i).substring((""+i).length())+": "+v.get(i));
	   System.out.print("choose method --> ");
	   ans=bin.readLine();
	   if(ans == null || ans.equals("")) System.exit(1);
	   ansi=Integer.parseInt(ans);
	}
	else
	   System.out.println("using "+v.get(0));
	newArgs[1]=(String)v.get(ansi);
	System.out.print("pattern (.) : ");
	ans=bin.readLine();
	if(ans == null || ans.equals(""))ans=".";
	newArgs[2]=ans;
	return newArgs;
   }
   public void doDel(Object[] oa, boolean force) throws Exception{
	if(! mayDelete){
	   System.out.println("delete disabled.");
	   return;
	}
	if(force){
	   System.out.println("-> methodserver delete: "+oa);
	   rms.invoke("doServerDel","ext.tools.ListAll",null, new Class[] {Object[].class}, new Object[] { oa });
	}
	else
	   for(int i=0;i<oa.length; i++){
	      if(! (oa[i] instanceof Persistable))
		   System.out.println("no Persistable: " + oa[i]);
		else if(oa[i] instanceof Iterated && ! VersionControlHelper.isLatestIteration((Iterated)oa[i]))
		   System.out.println("not latest Iteration: " + oa[i]);
		else{
		   System.out.println("delete: "+RF.getReferenceString((Persistable)oa[i]));
		   try{
			PersistenceHelper.manager.delete((Persistable)oa[i]);
		   }
		   catch(Exception err){
			if(err.toString().indexOf("there are effectivities associated") != -1){
			   ((EffManagedVersion)oa[i]).setEffVector(null);
			   EffGroupAssistant.replaceEffVector(((EffManagedVersion)oa[i]));
			   oa[i]=(Persistable)PersistenceHelper.manager.refresh((Persistable)oa[i]);
			   try{
				PersistenceHelper.manager.delete((Persistable)oa[i]);
			   }
			   catch(Exception err1){
				System.out.println("err="+err);
			   }
			}
			else{
			   System.out.println("err="+err);
			   throw err;
			}
		   }
		}
	   }
   }
   public static void doServerDel(Object[] oa) throws Exception{
	Transaction transaction = new Transaction();
	try {
	   transaction.start();
	   for(int i=0;i<oa.length; i++){
		if(! (oa[i] instanceof Persistable))
		   System.out.println("no Persistable from ListAll: " + oa[i]);
		else if(oa[i] instanceof Iterated && ! VersionControlHelper.isLatestIteration((Iterated)oa[i]))
		   System.out.println("not latest Iteration: " + oa[i]);
		else{
		   System.out.println("delete obj " + (new ReferenceFactory()).getReferenceString((Persistable)oa[i]) + " on behalf of ListAll");
		   try{
			PersistentObjectManager.getPom().remove((Persistable)oa[i]);
		   }
		   catch(Exception err){
		      err.printStackTrace();
		   }
		}
	   }
	   transaction.commit();
	}
	catch(Exception err){
	   transaction.rollback();
	   throw err;
	}
   }
   public static QueryResult doServerExpand(Persistable o) throws Exception{
	   System.out.println("expand obj " + o + " on behalf of ListAll");
	   return PersistenceServerHelper.manager.expand((Persistable)o, "ALL", wt.fc.BinaryLink.class, false);
   }

   public QueryResult serverQuery(QuerySpec q) throws Exception{
	if(noAuth)
	   return (QueryResult)RemoteMethodServer.getDefault().invoke("_serverQuery","ext.tools.ListAll",null, new Class[] {QuerySpec.class}, new Object[] { q });
	else
	   return PersistenceHelper.manager.find(q);
   }
   public static QueryResult _serverQuery(QuerySpec q) throws Exception{
	boolean old=SessionServerHelper.manager.setAccessEnforced(false);
	QueryResult qr = PersistenceHelper.manager.find(q);
	SessionServerHelper.manager.setAccessEnforced(old);
	return qr;
   }
   public static int rollback(String latest, String keep){
	try{
	   Iterated i1 = (Iterated) RF.getReference(latest).getObject();
	   Iterated i2 = (Iterated) RF.getReference(keep).getObject();
	   System.out.println("roll back from "+((WTObject)i1).getDisplayIdentity()+"."+i1.getIterationIdentifier().getValue()+" to "+((WTObject)i2).getDisplayIdentity()+"."+i2.getIterationIdentifier().getValue());
	   VersionControlHelper.service.rollback(i1,i2);
	   return 0;
	}
	catch(Exception hoppla){
	   hoppla.printStackTrace();
	}
	return 0;
   }
   public static int rollup(String from, String keep){
	try{
	   Iterated i1 = (Iterated) RF.getReference(from).getObject();
	   Iterated i2 = (Iterated) RF.getReference(keep).getObject();
	   System.out.println("roll up from "+((WTObject)i1).getDisplayIdentity()+i1.getIterationIdentifier().getValue()+" to "+((WTObject)i2).getDisplayIdentity()+"."+i2.getIterationIdentifier().getValue());
	   VersionControlHelper.service.rollup(i1,i2);
	   return 0;
	}
	catch(Exception hoppla){
	   hoppla.printStackTrace();
	}
	return 0;
   }
   public static int serializeIt(String obid){
	try{
	   Persistable p = (Persistable) RF.getReference(obid).getObject();
	   ObjectOutputStream oos = new ObjectOutputStream(System.out); // new FileOutputStream(residentSaved + group));
	   oos.writeObject(p);
	   oos.close();
	   return 0;
	}
	catch(Exception err){
	   err.printStackTrace();
	   return 1;
	}
   }
   private Method findMethod(String mn, Class c){
	Method m=null;
	//System.out.println("findMethod "+mn+" on "+c);
	try{
	   m = c.getMethod(mn,new Class[]{});
	}catch(Exception x1){} // try next ... name -> getName
	if(m == null && !mn.startsWith("get"))
	    mn = "get" + mn.substring(0, 1).toUpperCase() + mn.substring(1);
	try{
	   //System.out.println("try: "+mn);
	   m = c.getMethod(mn,new Class[]{});
	}catch(Exception x1){} // still no luck?
	if(m == null){
	   // try case insensitive
	   Method[] ml=c.getMethods(); //DeclaredMethods();
	   String mnl=mn.toLowerCase();
	   for(int i=0;i<ml.length;i++){
		if(ml[i].getName().toLowerCase().indexOf(mnl)>=0){
		   m=ml[i];
		   break;
		}
	   }
	}
	return m;
   }

   private String getEffInfo(EffManagedVersion e, String ind) throws Exception{
	if(rms == null)rms=RemoteMethodServer.getDefault();
      return (String) rms.invoke("_getEffInfo","ext.tools.ListAll",null, new Class[] {EffManagedVersion.class, String.class}, new Object[] { e, ind });
   }
   public static String _getEffInfo(EffManagedVersion e, String ind) throws Exception{
	StringBuffer b=new StringBuffer();
	QueryResult qeff=EffHelper.service.getEffs(e);
	Eff ef;
	EffRange r;
	Serializable send;
	String start,end;
	b.append(ind+"Effectivities raw:"+nl);
	WTObject efm;
	EffChangeAudit eca;
	while(qeff.hasMoreElements()){
	   ef=(Eff)qeff.nextElement();
	   b.append(ind+"   "+ef+" "+(efm=(WTObject)ef.getEffContext()));
	   if(efm!=null)
		b.append(" ("+efm.getDisplayIdentity()+")");
	   start=(r=ef.getRange()).getStart().toString().trim();
	   end=(send=r.getEnd())!=null ? send.toString().trim() : "";
	   b.append("  "+start+"-" +end+nl);
	   eca=ef.getCreation();
	   b.append(ind+"      created "+eca.getPersistInfo().getCreateStamp()+" by "+eca.getModifier().getName());
	   if((eca=ef.getDeletion()) != null)
		b.append("      deleted "+eca.getPersistInfo().getCreateStamp()+" by "+eca.getModifier().getName());
	   b.append(nl);
	}
	EffManagedVersion ee=EffGroupAssistant.populateEffVector(e);
	b.append(ind+"Effectivities:"+nl);
	Vector ve=ee.getEffVector();
	for(int i=0;ve!=null&&i<ve.size();i++){
	   EffGroup eg=(EffGroup)ve.get(i);
	   do{
	   b.append(ind+"context="+eg.getEffContext()+" at "+eg.getModifyStamp()+" range="+eg.getRange()+nl);
	   eg=(EffGroup)eg.getPredecessor();
	   }while(eg!=null);
	}
      return b.toString();
   }
   private static void printIBAs(Object o, String indent) throws Exception{
	System.out.println(indent+"IBAs:");
	Vector cgv;
	DefaultAttributeContainer container =(DefaultAttributeContainer)
	((IBAHolder)IBAValueHelper.service.refreshAttributeContainer((IBAHolder)o, null, null, null)).getAttributeContainer();
	if (container != null){
	   AbstractValueView valueview[] = container.getAttributeValues();
	   for(int i=0;i<valueview.length;i++){
		System.out.print(indent+"   ");
		System.out.print(valueview[i].getDefinition().getName());
		System.out.print(" -> ");
		System.out.print(valueview[i].getDefinition().getDisplayName());
		System.out.print("=");
		if(valueview[i] instanceof TimestampValueDefaultView)
		   System.out.println(((TimestampValueDefaultView)valueview[i]).getValue().toString().replaceAll("-",".").replaceFirst("..$",""));
		else if(valueview[i] instanceof ReferenceValueDefaultView)
		   System.out.println(((ReferenceValueDefaultView)valueview[i]).getLiteIBAReferenceable().getIBAReferenceableDisplayString().toString());
		else if(valueview[i] instanceof UnitValueDefaultView){
		   System.out.println(" "+((UnitValueDefaultView)valueview[i]).toUnit());
		}
		else
		   System.out.println(((AbstractContextualValueDefaultView)valueview[i]).getValueAsString());

	   } // loop ibas
	} // container not null
   }
   private static void printProcesses(Persistable p, String indent) throws Exception{
	System.out.println(indent+"Processes:");
      //for(Enumeration e = WfEngineHelper.service.getAssociatedProcesses(p, null, ((WTContained)p).getContainerReference()); e.hasMoreElements();){
      for(Enumeration e = WfEngineHelper.service.getAssociatedProcesses(p, null); e.hasMoreElements();){
	   WfProcess process = (WfProcess)e.nextElement();
	   String warn="";
	   if(!  ((WTContained)p).getContainerReference().equals(process.getContainerReference()))
	      warn=" (WARN: container="+process.getContainerName()+")";
	   //WfTemplateObjectReference templateRef = process.getTemplate();
	   System.out.println(indent+"   "+ToolUtils.getOR(process)+warn);
      }

   }
   private String getPathOccInfo(Persistable o, String ind) throws Exception{
	if(rms == null)rms=RemoteMethodServer.getDefault();
      return (String) rms.invoke("_getPathOccInfo","ext.tools.ListAll",null, new Class[] {String.class, String.class}, new Object[] { RF.getReferenceString(o), ind });
   }
   public static String _getPathOccInfo(String or, String ind) throws Exception{
	StringBuffer b=new StringBuffer();
	try{
	PartUsesOccurrence puo;
	QueryResult puor = OccurrenceHelper.service.getUsesOccurrences((PartPathOccurrence)RF.getReference(or).getObject());
	while(puor.hasMoreElements()){
	   puo=(PartUsesOccurrence)puor.nextElement();
	   b.append(nl+ind+"   PartUsesOccurrence wt.part.PartUsesOccurrence:"+PersistenceHelper.getObjectIdentifier((Persistable)puo).getId());
	   WTPartUsageLink ul=(WTPartUsageLink)puo.getLink();
	   b.append(nl+ind+"      WTPartUsageLink "+RF.getReferenceString(ul));
	   b.append(nl+ind+"         "+ToolUtils.prettyPrint(ul.getAllObjects()).replaceAll("[\\x0a\\x0d]+"," "));
	}
	}
	catch(Exception sowas){
	   ByteArrayOutputStream bos=new ByteArrayOutputStream();
	   sowas.printStackTrace(new PrintStream(bos));
	   b.append("Exception:"+nl);
	   b.append(bos.toString());
	}
	return b.toString();
   }
}
