/* * Copyright 2011 Uwe Krueger. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.mandelsoft.mand; import com.mandelsoft.util.Utils; import java.io.File; /** * * @author Uwe Krueger */ public class MandelName extends DefaultElementName<MandelName> { static public final String ROOT_NAME="0"; static public final MandelName ROOT=new MandelName(ROOT_NAME); protected String effective; public MandelName(String n) { super(compress(n)); if (label!=null) { n=name.substring(0,name.length()-label.length()-1); } this.effective=uncompress(n); } MandelName(String n, String label) { String tmp=label(n); if (tmp!=null) { throw new IllegalArgumentException("no remote area for explicitly labeled name"); } this.name=addLabel(compress(n),label); this.label=label; this.effective=uncompress(n); } public String getEffective() { return effective; } public String getSubAreaName() { String n=effective.substring(effective.length()-1); if (getLabel()==null) return n; return n+LABEL_START+getLabel(); } public char getSubAreaChar() { return effective.charAt(effective.length()-1); } public boolean isRoot() { //return isRoot(name); return name.equals(ROOT_NAME); } public boolean isAbove(MandelName n) { if (isRoot()) return true; if (n.effective.startsWith(effective)) { int start=effective.length(); int ix=n.effective.indexOf(LABEL_START, start); if (ix<0) { return Utils.equals(n.label, label); } else { start=ix; ix=n.effective.indexOf(LABEL_END, start+1); if (ix>=0) { return Utils.equals(n.effective.substring(start+1,ix),label); } else { return Utils.equals(n.effective.substring(start+1), label); } } } return false; } public boolean isHigher(MandelName n) { if (isRoot()) return !n.equals(this); return (isAbove(n) && !name.equals(n.name)); // String sn=n.getEffective(); // String st=getEffective(); // return sn.startsWith(st) && sn.length()!=st.length(); } public MandelName sub() { if (label==null) { return new MandelName((isRoot()?"":getName())+"a"); } else { return new MandelName(getName()+LABEL_END_STR+"a"); } } public MandelName sub(char sub) { if (label==null) { return new MandelName((isRoot()?"":getName())+sub); } else { return new MandelName(getName()+LABEL_END_STR+sub); } } public MandelName subAt(String nlabel) { if (nlabel==null) return sub(); if (label==null) { if (isRoot()) { return new MandelName("a",nlabel); } throw new IllegalArgumentException("no remote area for local name"); } else { if (nlabel.equals(label)) { return new MandelName((isRoot()?"":effective)+"a",nlabel); } else { return new MandelName(getName()+LABEL_END_STR+"a"+LABEL_START_STR+nlabel); } } } public MandelName sub(MandelName n) { if (!isHigher(n)) { throw new IllegalArgumentException(getName()+" is not a parent of "+ n.getName()); } int l,e,ix,is; String nlabel; String base; if (isRoot()) { l=0; e=1; base=""; } else { l=effective.length(); e=0; base=effective; } if (n.effective.charAt(l)==LABEL_START) { e=n.effective.indexOf(LABEL_END,l+1)+2; } if (e>0) { ix=n.effective.indexOf(LABEL_START,e); if (ix>=0) { is=n.effective.indexOf(LABEL_END,ix+1); if (is>=0) { nlabel=n.effective.substring(ix+1,is); } else { nlabel=n.effective.substring(ix+1); } } else nlabel=n.label; } else { e=l+1; nlabel=label; } return new MandelName(base+n.effective.substring(l,e),nlabel); } public MandelName next() { if (isRoot()) return null; String eff=effective; char n=next(eff.charAt(eff.length()-1)); if (n==0) return null; return new MandelName(eff.substring(0,eff.length()-1)+Character.toString(n), label); } public MandelName prev() { if (isRoot()) return null; String eff=effective; char n=prev(eff.charAt(eff.length()-1)); if (n==0) return null; return new MandelName(eff.substring(0,eff.length()-1)+Character.toString(n), label); } public MandelName getParentName() { if (isRoot()) return null; String e=effective; String nlabel=label; int l=e.length()-1; if (l==0) { e=ROOT_NAME; nlabel=null; } else { e=e.substring(0,l); if (e.charAt(l-1)==LABEL_END) { int ix=e.lastIndexOf(LABEL_START); nlabel=e.substring(ix+1,l-1); e=e.substring(0,ix); } } return new MandelName(e,nlabel); } /////////////////////////////////////////////////////////////////////// @Override public int compareTo(MandelName o) { if (o==null) return 1; int c=effective.compareTo(o.effective); if (c!=0) return c; if (label==null) { if (o.label==null) return 0; return -1; } return label.compareTo(o.label); } /////////////////////////////////////////////////////////////////////// // general name utils /////////////////////////////////////////////////////////////////////// static public String compress(String n) { int cnt=0; char r=0; char l=0; boolean label=false; boolean first=false; boolean digit=false; if (isRoot(n)) return n; StringBuffer sb=new StringBuffer(); // if (n.startsWith(ROOT_NAME+"@")) { // sb.append(ROOT_NAME); // sb.append(LABEL_START); // n=n.substring(ROOT_NAME.length()+1); // label=true; // first=true; // } for (char c:n.toCharArray()) { if (label) { if (first) { if (!Character.isJavaIdentifierStart(c)) { throw new IllegalArgumentException(n+" is no valid mandel name"); } first=false; } sb.append(c); if (c==LABEL_END) label=false; else { if (!Character.isJavaIdentifierPart(c)) { throw new IllegalArgumentException(n+" is no valid mandel name"); } } continue; } if (c==r) cnt++; else { if (r>0) { appendC(cnt,r,sb); l=r; r=0; cnt=0; digit=false; } if (Character.isDigit(c)) { digit=true; cnt=cnt*10+c-'0'; } else { if (c==LABEL_START) { if (sb.length()==0) { throw new IllegalArgumentException(n+" is no valid mandel name"); } sb.append(c); first=true; label=true; l=0; } else { if (!isValid(c)) { throw new IllegalArgumentException(n+" is no valid mandel name"); } if (l==c) { throw new IllegalArgumentException(n+" is no valid mandel name"); } l=r; r=c; if (cnt==0) { if (digit) { throw new IllegalArgumentException(n+" is no valid mandel name"); } cnt=1; } } } } } if (r>0) appendC(cnt,r,sb); else { if (cnt>0) { throw new IllegalArgumentException(n+" is no valid mandel name"); } } return sb.toString(); } static public String uncompress(String n) { int cnt=0; char r=0; boolean label=false; boolean first=false; boolean digit=false; if (isRoot(n)) return n; StringBuffer sb=new StringBuffer(); // if (n.startsWith(ROOT_NAME+"@")) { // sb.append(ROOT_NAME); // sb.append(LABEL_START); // n=n.substring(ROOT_NAME.length()+1); // label=true; // first=true; // } for (char c:n.toCharArray()) { if (label) { if (first) { if (!Character.isJavaIdentifierStart(c)) { throw new IllegalArgumentException(n+" is no valid mandel name"); } first=false; } sb.append(c); if (c==LABEL_END) label=false; else { if (!Character.isJavaIdentifierPart(c)) { throw new IllegalArgumentException(n+" is no valid mandel name"); } } continue; } if (c==r) cnt++; else { if (r>0) { appendU(cnt,r,sb); r=0; cnt=0; } if (Character.isDigit(c)) { cnt=cnt*10+c-'0'; } else { if (c==LABEL_START) { if (sb.length()==0) { throw new IllegalArgumentException(n+" is no valid mandel name"); } sb.append(c); first=true; label=true; } else { if (!isValid(c)) { throw new IllegalArgumentException(n+" is no valid mandel name"); } r=c; if (cnt==0) cnt=1; } } } } if (r>0) appendU(cnt,r,sb); return sb.toString(); } public static boolean isRoot(String n) { if (n.equals(ROOT_NAME)) return true; // if (n.startsWith(ROOT_NAME)) { // int ix=ROOT_NAME.length(); // if (n.charAt(ix++)==LABEL_START) { // if (ix==n.length() || !Character.isJavaIdentifierStart(n.charAt(ix++))) return false; // while (ix<n.length()) { // if (!Character.isJavaIdentifierPart(n.charAt(ix++))) return false; // } // return true; // } // } return false; } public static boolean isValid(char c) { return (c>='a' && c<='z'); // (c>='A' && c<='Z') not possible on Windows } public static char next(char c) { if (isValid(c) && isValid((char)(c+1))) return (char)(c+1); return (char)0; } public static char prev(char c) { if (isValid(c) && isValid((char)(c-1))) return (char)(c-1); return (char)0; } public static boolean isMandelName(String n) { try { new MandelName(n); return true; } catch (IllegalArgumentException e) { return false; } } public static MandelName create(String n) { try { return new MandelName(n); } catch (IllegalArgumentException e) { return null; } } public static MandelName create(File f) { try { String base=f.getName(); int ix=base.lastIndexOf('.'); if (ix>=0) base=base.substring(0,ix); ix=base.indexOf('-'); if (ix>=0) base=base.substring(0,ix); return new MandelName(base); } catch (IllegalArgumentException e) { return null; } } /////////////////////////////////////////////////////////////////////// // private name utils /////////////////////////////////////////////////////////////////////// private static void appendC(int cnt, char c, StringBuffer sb) { if (cnt>1) sb.append(cnt); if (c==0 || cnt==0) throw new IllegalArgumentException(sb+" is no valid mandel name"); sb.append(c); } private static void appendU(int cnt, char c, StringBuffer sb) { if (c!=0) { while (cnt-->0) { sb.append(c); } } else throw new IllegalArgumentException(sb+" is no valid mandel name"); } /////////////////////////////////////////////////////////////////////// // main (test) /////////////////////////////////////////////////////////////////////// private interface Operation { String op(String a); String getOp(); } private static class Compress implements Operation { public String op(String a) { return compress(a); } public String getOp() { return "compress"; } } private static class UnCompress implements Operation { public String op(String a) { return uncompress(a); } public String getOp() { return "uncompress"; } } private static class SubAt implements Operation { String label; public String op(String a) { MandelName mn=new MandelName(a); return mn.subAt(label).getName(); } public String getOp() { return "sub("+label+")"; } } private static class Above implements Operation { MandelName base; public void setBase(String base) { this.base=new MandelName(base); } public String op(String a) { MandelName mn=new MandelName(a); return mn.isAbove(base)?"true":"false"; } public String getOp() { return "above("+base+")"; } } private static class Down implements Operation { MandelName base; public void setBase(String base) { this.base=new MandelName(base); } public String op(String a) { MandelName mn=new MandelName(a); return mn.sub(base).getName(); } public String getOp() { return "down("+base+")"; } } private static class Parent implements Operation { public String op(String a) { return new MandelName(a).getParentName().getName(); } public String getOp() { return "parent"; } } static Operation compress=new Compress(); static Operation uncompress=new UnCompress(); static SubAt sub=new SubAt(); static Above above=new Above(); static Down down=new Down(); static Parent parent=new Parent(); static int failed=0; private static boolean check(Operation op, String arg, String exp) { try { String res=op.op(arg); if (res.equals(exp)) { System.out.println(op.getOp()+": "+arg+" -> "+res+" (OK)"); return true; } else { System.out.println( op.getOp()+": "+arg+" -> "+res+" (FAILED) expected "+exp); failed++; return false; } } catch (IllegalArgumentException ex) { if (exp!=null) { failed++; System.out.println(op.getOp()+": "+arg+" -> <illegal> (FAILED) expected " +exp+" ("+ex.getMessage()+")"); } else { System.out.println(op.getOp()+": "+arg+" -> <illegal> (OK)"); } return exp==null; } } static public int test() { System.out.println("starting MandelName tests..."); failed=0; check(compress,"0","0"); check(compress,"a","a"); check(compress,"ab","ab"); check(compress,"abc","abc"); check(compress,"aa","2a"); check(compress,"abbc","a2bc"); check(compress,"aabc","2abc"); check(compress,"aabbc","2a2bc"); check(compress,"aabbcc","2a2b2c"); check(compress,"2abbcc","2a2b2c"); check(compress,"aa2bcc","2a2b2c"); check(compress,"aabb2c","2a2b2c"); check(compress,"a@x","a@x"); check(compress,"a1",null); check(compress,"10",null); check(uncompress,"0","0"); check(uncompress,"a","a"); check(uncompress,"ab","ab"); check(uncompress,"abc","abc"); check(uncompress,"aa","aa"); check(uncompress,"abbc","abbc"); check(uncompress,"aabc","aabc"); check(uncompress,"aabbc","aabbc"); check(uncompress,"aabbcc","aabbcc"); check(uncompress,"2abbcc","aabbcc"); check(uncompress,"aa2bcc","aabbcc"); check(uncompress,"aabb2c","aabbcc"); check(uncompress,"a@x","a@x"); check(uncompress,"a@x@y",null); //check("compress","2aa2a",compress("2aa2a"),"5a"); check(uncompress,"2aa2a","aaaaa"); check(uncompress,"2a@laber~2a","aa@laber~aa"); check(uncompress,"aa@laber~aa","aa@laber~aa"); check(uncompress,"1@laber",null); check(uncompress,"aa@5aber~aa",null); check(uncompress,"aa@la@er~aa",null); check(compress,"0aabb2c",null); check(compress,"a1abb2c",null); check(compress,"a0abb2c",null); check(compress,"aa0b2c",null); check(compress,"2a2a",null); //check(compress,"0@laber","0@laber"); check(compress,"0@laber",null); //check(compress,"0@laber~2a","0@laber~2a"); check(compress,"0@laber~2a",null); check(compress,"2a@laber~2a","2a@laber~2a"); check(compress,"aa@laber~aa","2a@laber~2a"); check(compress,"1@laber",null); check(compress,"aa@5aber~aa",null); check(compress,"aa@la@er~aa",null); //////////////////////////////////////////////////////////// sub.label=null; check(sub,"0","a"); check(sub,"a","2a"); //check(sub,"0@laber","0@laber~a"); //check(sub,"0@laber~a","0@laber~2a"); check(sub,"a@laber","a@laber~a"); check(sub,"a@laber~a","a@laber~2a"); sub.label="laber"; //check(sub,"0@laber","a@laber"); check(sub,"0","a@laber"); check(sub,"a@laber","2a@laber"); sub.label="other"; //check(sub,"0@laber","0@laber~a@other"); check(sub,"a@laber","a@laber~a@other"); check(sub,"a@laber~a",null); //////////////////////////////////////////////////////////// above.setBase("5a"); check(above,"4abc","false"); check(above,"6a","false"); check(above,"5a","true"); check(above,"4a","true"); above.setBase("5a@laber"); check(above,"4a","false"); // no common root check(above,"4a@other","false"); check(above,"4a@laber","true"); check(above,"4a@laber~a","false"); check(above,"4a@laber~a@other","false"); above.setBase("5a@laber~2a"); check(above,"4a@laber~a@other","false"); check(above,"4a@laber~a","false"); check(above,"5a@laber~a","true"); above.setBase("5a@laber~2a@other"); check(above,"4a@laber~a@other","false"); check(above,"5a@laber~a@other","true"); check(above,"5a@laber","true"); //////////////////////////////////////////////////////////// down.setBase("abc"); check(down,"0","a"); check(down,"a","ab"); check(down,"ab","abc"); check(down,"abc",null); check(down,"b",null); down.setBase("abc@laber"); check(down,"0","a@laber"); check(down,"a@laber","ab@laber"); check(down,"ab@laber","abc@laber"); down.setBase("abc@laber~def"); //check(down,"0@laber","a@laber"); check(down,"0","a@laber"); check(down,"a@laber","ab@laber"); check(down,"ab@laber","abc@laber"); check(down,"abc@laber","abc@laber~d"); check(down,"abc@laber~d","abc@laber~de"); down.setBase("abc@laber~def@other"); check(down,"a@laber","ab@laber"); check(down,"ab@laber","abc@laber"); check(down,"abc@laber","abc@laber~d@other"); check(down,"abc@laber~d@other","abc@laber~de@other"); check(down,"abc@laber~d",null); down.setBase("abc@laber~def@other~gh"); check(down,"a@laber","ab@laber"); check(down,"ab@laber","abc@laber"); check(down,"abc@laber","abc@laber~d@other"); check(down,"abc@laber~def@other","abc@laber~def@other~g"); check(down,"abc@laber~d",null); check(down,"abc@laber~def",null); //////////////////////////////////////////////////////////// check(parent,"a","0"); check(parent,"ab","a"); check(parent,"a@laber","0"); check(parent,"ab@laber","a@laber"); check(parent,"ab@laber~ab","ab@laber~a"); check(parent,"ab@laber~a","ab@laber"); check(parent,"ab@laber~ab@other","ab@laber~a@other"); check(parent,"ab@laber~a@other","ab@laber"); System.out.println("failed: "+failed); return failed; } static public void main(String[] args) { test(); } }