// KHSClient.java // Written by Walter Piechulla in August 1999 for the seminar "Hypermedia" // To read this code, set tab position of your editor to 2 (tab represented by 2 spaces) !!! // This applet was written with Microsoft Visual J++ 1.1 (oh, what a scandal!). // Let me explain: I did it for practical reasons. // The Developer Studio is a very convenient programming workplace, and I was used to it // (I had done MFC and API-level programming with Visual C++). Visual J++ 1.1 is based // on the 1.0.2 version of JDK which is 'outdated' compared to the up to date Java 2 release // (formerly known as Java 1.2). But this means, that the bytecode produced by J++ 1.1 runs on // virtually EVERY browser's JVM. At the time this is written (1999), no browser on the market // has a Java 2 compatible JVM. Writing Java 2 Applets nowadays would mean forcing users to // install the browser plugin from Sun. While this is perfectly ok for serious intranet // applications, it would be complete nonsense for a simple demonstration like KHSClient. import java.util.*; import java.awt.*; import java.net.*; import java.io.*; import java.applet.Applet; class Node { String label; String theURL; Rectangle rc; Point center; boolean pushed; boolean selected; boolean hasContent; boolean isTerminal; boolean isUpper; boolean isDowner; public Node() { label = new String(); theURL = new String(); rc = new Rectangle(); center = new Point(0,0); pushed = false; hasContent = true; isTerminal = false; isUpper = false; isDowner = false; selected = false; } } class GrandDaughter extends Node { public GrandDaughter() { // Nothing to do } } class Daughter extends Node { Vector GrandDaughters; int distance2root; public Daughter() { GrandDaughters = new Vector(10,5); } } class Mother extends Node { Vector Daughters; public Mother() { Daughters = new Vector(10,5); } } class LabelsManager { Vector deadRectangles; boolean justPastDaughter; public LabelsManager() { deadRectangles = new Vector(10,5); justPastDaughter = false; } private Rectangle gloved(Rectangle naked,int gloveSize) { Rectangle withGlove = new Rectangle(); withGlove.reshape(naked.x,naked.y,naked.width,naked.height); withGlove.grow(gloveSize,gloveSize); return withGlove; } private boolean anyCollision(Rectangle rc,int gloveSize) { for (Enumeration enum = deadRectangles.elements();enum.hasMoreElements();) { Rectangle avoidMe = (Rectangle) enum.nextElement(); if (avoidMe.intersects(gloved(rc,gloveSize))) return true; } return false; } // To understand this you must know that a Rectangle comes in with its true dimensions, // but centered on its predecessor (to be improved) public Rectangle getAnotherLabelRc(int direction,Rectangle preparedRc,Rectangle avoidThisRc,int gloveSize) { Rectangle resultRc = new Rectangle(preparedRc.x,preparedRc.y,preparedRc.width,preparedRc.height); switch (direction) { case 3: // == going left down == drawing daughter == refresh justPastDaughter = true; deadRectangles.removeAllElements(); deadRectangles.addElement(gloved(avoidThisRc,gloveSize)); while (anyCollision(resultRc,gloveSize)) { resultRc.translate(-4,1); // Go to the left, go downwards } break; case 1: // Go to the right, go upwards if (justPastDaughter) { deadRectangles.removeAllElements(); justPastDaughter = false; } deadRectangles.addElement(gloved(preparedRc,gloveSize)); while (anyCollision(resultRc,gloveSize)) { resultRc.translate(2,-1); } break; case 2: // Go to the right, go downwards if (justPastDaughter) { deadRectangles.removeAllElements(); justPastDaughter = false; } deadRectangles.addElement(gloved(preparedRc,gloveSize)); while (anyCollision(resultRc,gloveSize)) { resultRc.translate(2,1); } break; } return resultRc; } } class GraphPanel extends Panel implements Runnable { public Mother theMother = new Mother(); StringBuffer emergencyStringBuf = new StringBuffer(); boolean fatalError = false; int antiCollisionMemorizer = 0; boolean newLayout = true; Image contentSymbol; Image upperSymbol; Image downerSymbol; Image offscreen; Dimension offscreensize; Graphics offgraphics; final Color backgroundColor = new Color(172,160,153); final Color standardAbgetoentesWeiss = new Color(255,251,240); // Marcellus Buchheit: Windows Programmierbuch p.83 Color leveledColors[] = new Color[9]; Frame BrowserFrame_; LabelsManager labelsManager = new LabelsManager(); KHSClient graphObj; Thread relaxer; GraphPanel(KHSClient graphObj) { this.graphObj = graphObj; } public void run() { while (true) { relax(); try { Thread.sleep(100); } catch (InterruptedException e) { break; } } } synchronized void relax() { repaint(); } public synchronized void update(Graphics g) { Dimension d = size(); antiCollisionMemorizer = 0; // Rewind avoid-predecessor-logic of getNiceNonIntersectingRc-method int focusLevel; // How far away ist the daughter from root // Offscreen graphics context if ((offscreen == null) || (d.width != offscreensize.width) || (d.height != offscreensize.height)) { offscreen = createImage(d.width,d.height); offscreensize = d; offgraphics = offscreen.getGraphics(); offgraphics.setFont(getFont()); } offgraphics.setColor(getBackground()); offgraphics.fillRect(0,0,d.width,d.height); FontMetrics fm = offgraphics.getFontMetrics(); if (!fatalError) { if (newLayout) { theMother.center.x = d.width/10*6; theMother.center.y = d.height/11; sizeAndPlaceTheLabel(theMother,fm,1); } // Knowing that there is actually only one daughter: Daughter soleDaughter = (Daughter) theMother.Daughters.elementAt(0); focusLevel = soleDaughter.distance2root; if (newLayout) { passCenter(theMother,soleDaughter); sizeAndPlaceTheLabel(soleDaughter,fm,2); getNiceNonIntersectingRc(soleDaughter.rc,theMother.rc,5,true); updateCenter(soleDaughter); } offgraphics.setColor(Color.darkGray); offgraphics.drawLine(theMother.center.x,theMother.center.y,soleDaughter.center.x,soleDaughter.center.y); drawNode(offgraphics,leveledColors[focusLevel],fm,theMother,theMother.pushed); drawNode(offgraphics,leveledColors[focusLevel+1],fm,soleDaughter,soleDaughter.pushed); boolean firstGrandDaughter = true; Node lastNode = new Node(); Node lastLastNode = new Node(); lastLastNode = theMother; lastNode = soleDaughter; // Conection lines, some computations for (Enumeration enum = soleDaughter.GrandDaughters.elements();enum.hasMoreElements();) { GrandDaughter grandDaughter = (GrandDaughter) enum.nextElement(); if (newLayout) { passCenter(lastNode,grandDaughter); sizeAndPlaceTheLabel(grandDaughter,fm,3); if (firstGrandDaughter) { getNiceNonIntersectingRc(grandDaughter.rc,soleDaughter.rc,80,true); firstGrandDaughter = false; } else { getNiceNonIntersectingRc(grandDaughter.rc,soleDaughter.rc,2,false); } updateCenter(grandDaughter); } offgraphics.setColor(Color.darkGray); offgraphics.drawLine(soleDaughter.center.x,soleDaughter.center.y,grandDaughter.center.x,grandDaughter.center.y); lastLastNode = lastNode; lastNode = grandDaughter; } // Draw labels for (Enumeration enum = soleDaughter.GrandDaughters.elements();enum.hasMoreElements();) { GrandDaughter grandDaughter = (GrandDaughter) enum.nextElement(); drawNode(offgraphics,leveledColors[focusLevel+2],fm,grandDaughter,grandDaughter.pushed); } // Redraw daughter, may be crossed by lines drawn drawNode(offgraphics,leveledColors[focusLevel+1],fm,soleDaughter,soleDaughter.pushed); } else { // Handling of fatal errors offgraphics.setColor(Color.black); offgraphics.drawString("Please notify walter.piechulla@psychologie.uni-regensburg.de of this problem:",2,10); offgraphics.drawString(emergencyStringBuf.toString(),2,30); } g.drawImage(offscreen,0,0,null); newLayout = false; } // Position: 1=up, 2=middle, 3=down private void sizeAndPlaceTheLabel(Node node,FontMetrics fm,int position) { node.rc.width = fm.stringWidth(node.label) + 10; if (node.hasContent) { node.rc.width += 20; } // Detect navigation role of label (if any): "upper" or "downer" switch(position) { case 1: // Top: is an upper, if not root if (node.label.equals("?")) { node.isUpper = false; node.isDowner = false; } else { node.isUpper = true; node.isDowner = false; } break; case 3: // Bottom: is a downer, if not a terminal unit if (node.isTerminal) { node.isDowner = false; node.isUpper = false; } else { node.isDowner = true; node.isUpper = false; } break; default: break; } if (node.isUpper || node.isDowner) { node.rc.width += 20; } node.rc.height = fm.getHeight() + 4; node.rc.x = node.center.x - node.rc.width/2; node.rc.y = node.center.y - node.rc.height/2; } private void updateCenter(Node node) { node.center.x = node.rc.x + node.rc.width/2; node.center.y = node.rc.y + node.rc.height/2; } private void passCenter(Node n1,Node n2) { n2.center.x = n1.center.x; n2.center.y = n1.center.y; } private void drawNode(Graphics g,Color color,FontMetrics fm,Node node,boolean pushed) { if (node.selected) { g.setColor(Color.white); } else { g.setColor(color); } g.fillRect(node.rc.x,node.rc.y,node.rc.width,node.rc.height); Point leftTop = new Point(node.rc.x,node.rc.y); Point rightBottom = new Point(node.rc.x+(node.rc.width-1),node.rc.y+(node.rc.height-1)); if (pushed) { g.setColor(Color.darkGray); } else { g.setColor(standardAbgetoentesWeiss); } g.drawLine(leftTop.x,rightBottom.y,leftTop.x,leftTop.y); g.drawLine(leftTop.x,leftTop.y,rightBottom.x,leftTop.y); if (pushed) { g.setColor(standardAbgetoentesWeiss); } else { g.setColor(Color.darkGray); } g.drawLine(leftTop.x+1,rightBottom.y,rightBottom.x,rightBottom.y); g.drawLine(rightBottom.x,rightBottom.y,rightBottom.x,leftTop.y+1); g.setColor(Color.black); if (pushed) // Draw shifted one pixel to the right and one pixel down { if (node.hasContent) { if (node.isUpper) { g.drawImage(upperSymbol,1 + node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2 - 7),fm.getHeight()+4,fm.getHeight()+8,this); g.drawImage(contentSymbol,1 + 20 + node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2),fm.getHeight()+4,fm.getHeight()+4,this); g.drawString(node.label,1 + 40 + node.center.x - (node.rc.width-10)/2,(node.center.y - (node.rc.height-4)/2) + fm.getAscent()); } else if (node.isDowner) { g.drawImage(downerSymbol,1 + node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2),fm.getHeight()+4,fm.getHeight()+4,this); g.drawImage(contentSymbol,1 + 20 + node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2),fm.getHeight()+4,fm.getHeight()+4,this); g.drawString(node.label,1 + 40 + node.center.x - (node.rc.width-10)/2,(node.center.y - (node.rc.height-4)/2) + fm.getAscent()); } else { g.drawImage(contentSymbol,1 + node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2),fm.getHeight()+4,fm.getHeight()+4,this); g.drawString(node.label,20 + 1 + node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2) + fm.getAscent()); } } else { if (node.isUpper) { g.drawImage(upperSymbol,1 + node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2 - 7),fm.getHeight()+4,fm.getHeight()+8,this); g.drawString(node.label,1 + 20 + node.center.x - (node.rc.width-10)/2,(node.center.y - (node.rc.height-4)/2) + fm.getAscent()); } else if (node.isDowner) { g.drawImage(downerSymbol,1 + node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2),fm.getHeight()+4,fm.getHeight()+4,this); g.drawString(node.label,1 + 20 + node.center.x - (node.rc.width-10)/2,(node.center.y - (node.rc.height-4)/2) + fm.getAscent()); } else { g.drawString(node.label,1 + node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2) + fm.getAscent()); } } } else { if (node.hasContent) { if (node.isUpper) { g.drawImage(upperSymbol,node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2),fm.getHeight()-1,fm.getHeight()-1,this); g.drawImage(contentSymbol,20 + node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2),fm.getHeight()-1,fm.getHeight()-1,this); g.drawString(node.label,40 + node.center.x - (node.rc.width-10)/2,(node.center.y - (node.rc.height-4)/2) + fm.getAscent()); } else if (node.isDowner) { g.drawImage(downerSymbol,node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2),fm.getHeight()-1,fm.getHeight()-1,this); g.drawImage(contentSymbol,20 + node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2),fm.getHeight()-1,fm.getHeight()-1,this); g.drawString(node.label,40 + node.center.x - (node.rc.width-10)/2,(node.center.y - (node.rc.height-4)/2) + fm.getAscent()); } else { g.drawImage(contentSymbol,node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2),fm.getHeight()-1,fm.getHeight()-1,this); g.drawString(node.label,20 + node.center.x - (node.rc.width-10)/2,(node.center.y - (node.rc.height-4)/2) + fm.getAscent()); } } else { if (node.isUpper) { g.drawImage(upperSymbol,node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2),fm.getHeight()-1,fm.getHeight()-1,this); g.drawString(node.label,20 + node.center.x - (node.rc.width-10)/2,(node.center.y - (node.rc.height-4)/2) + fm.getAscent()); } else if (node.isDowner) { g.drawImage(downerSymbol,node.center.x - (node.rc.width-10)/2,1 + (node.center.y - (node.rc.height-4)/2),fm.getHeight()-1,fm.getHeight()-1,this); g.drawString(node.label,20 + node.center.x - (node.rc.width-10)/2,(node.center.y - (node.rc.height-4)/2) + fm.getAscent()); } else { g.drawString(node.label,node.center.x - (node.rc.width-10)/2,(node.center.y - (node.rc.height-4)/2) + fm.getAscent()); } } } } private void getNiceNonIntersectingRc(Rectangle dstRc,Rectangle avoidRc,int push,boolean drawDaughter) { int decision; if (drawDaughter) { decision = 3; } else { if (antiCollisionMemorizer < 2) { decision = 1; antiCollisionMemorizer++; } else { decision = 2; antiCollisionMemorizer++; if (antiCollisionMemorizer == 4) antiCollisionMemorizer = 0; } } Rectangle templateRc = labelsManager.getAnotherLabelRc(decision,dstRc,avoidRc,push); dstRc.reshape(templateRc.x,templateRc.y,templateRc.width,templateRc.height); } public synchronized boolean mouseDown(Event evt,int x,int y) { for (Enumeration enum = theMother.Daughters.elements();enum.hasMoreElements();) { Daughter daughter = (Daughter) enum.nextElement(); for (Enumeration enum2 = daughter.GrandDaughters.elements();enum2.hasMoreElements();) { GrandDaughter grandDaughter = (GrandDaughter) enum2.nextElement(); if (grandDaughter.rc.inside(x,y)) { grandDaughter.pushed = true; grandDaughter.selected = true; } else { grandDaughter.selected = false; } } } if (theMother.rc.inside(x,y)) { theMother.pushed = true; theMother.selected = true; } else { theMother.selected = false; } // One single daughter only Daughter soleDaughter = (Daughter) theMother.Daughters.elementAt(0); if (soleDaughter.rc.inside(x,y)) { soleDaughter.pushed = true; soleDaughter.selected = true; } else { soleDaughter.selected = false; } repaint(); // Immediately return true; } // Shape cursor to hand when it is located on a label public synchronized boolean mouseMove(Event evt,int x,int y) { for (Enumeration enum = theMother.Daughters.elements();enum.hasMoreElements();) { Daughter daughter = (Daughter) enum.nextElement(); for (Enumeration enum2 = daughter.GrandDaughters.elements();enum2.hasMoreElements();) { GrandDaughter grandDaughter = (GrandDaughter) enum2.nextElement(); if (grandDaughter.rc.inside(x,y)) { BrowserFrame_.setCursor(BrowserFrame_.HAND_CURSOR); return true; } } } if (theMother.rc.inside(x,y)) { BrowserFrame_.setCursor(BrowserFrame_.HAND_CURSOR); return true; } // One single daughter only Daughter soleDaughter = (Daughter) theMother.Daughters.elementAt(0); if (soleDaughter.rc.inside(x,y)) { BrowserFrame_.setCursor(BrowserFrame_.HAND_CURSOR); return true; } BrowserFrame_.setCursor(BrowserFrame_.DEFAULT_CURSOR); return true; } public synchronized boolean mouseUp(Event evt, int x, int y) { for (Enumeration enum = theMother.Daughters.elements();enum.hasMoreElements();) { Daughter daughter = (Daughter) enum.nextElement(); for (Enumeration enum2 = daughter.GrandDaughters.elements();enum2.hasMoreElements();) { GrandDaughter grandDaughter = (GrandDaughter) enum2.nextElement(); if (grandDaughter.pushed) { grandDaughter.pushed = false; try { URL metaURL = new URL(grandDaughter.theURL); URL bareUnitURL = new URL(graphObj.meta2bare(grandDaughter.theURL)); if (grandDaughter.hasContent) { graphObj.getAppletContext().showDocument(bareUnitURL,"vhlk"); } else { URL logoURL = new URL(graphObj.KHSRLogopage); graphObj.getAppletContext().showDocument(logoURL,"vhlk"); } if (!grandDaughter.isTerminal) { graphObj.CurrentDeepness++; graphObj.reInit(metaURL,grandDaughter.label,grandDaughter.theURL,grandDaughter.hasContent); } } catch (MalformedURLException e) { troubleShooting("MalformedURLException beim öffnen von " + grandDaughter.theURL); } } } } if (theMother.pushed) { theMother.pushed = false; try { URL metaURL = new URL(theMother.theURL); URL bareUnitURL = new URL(graphObj.meta2bare(theMother.theURL)); if (0 == theMother.label.compareTo("?")) // Special case { graphObj.getAppletContext().showDocument(metaURL,"vhlk"); } else if (0 == theMother.label.compareTo(graphObj.getParameter("initiallabel"))) { graphObj.CurrentDeepness--; URL accessURL = new URL(graphObj.TraditionalAccessToEntryPage.toString()); graphObj.getAppletContext().showDocument(accessURL,"vhlk"); graphObj.reInit(metaURL,theMother.label,theMother.theURL,true); } else { graphObj.CurrentDeepness--; URL logoURL = new URL(graphObj.KHSRLogopage); graphObj.getAppletContext().showDocument(logoURL,"vhlk"); graphObj.reInit(metaURL,theMother.label,theMother.theURL,false); } } catch (MalformedURLException e) { troubleShooting("MalformedURLException beim öffnen von " + theMother.theURL); } } // Again using my knowledge, that there is only one daughter... Daughter soleDaughter = (Daughter) theMother.Daughters.elementAt(0); if (soleDaughter.pushed) { soleDaughter.pushed = false; // Display alraedy ok, just load daughter try { if (soleDaughter.hasContent) { if (0 == soleDaughter.label.compareTo(graphObj.getParameter("initiallabel"))) { URL accessURL = new URL(graphObj.TraditionalAccessToEntryPage.toString()); graphObj.getAppletContext().showDocument(accessURL,"vhlk"); } else { URL bareUnitURL = new URL(graphObj.meta2bare(soleDaughter.theURL)); graphObj.getAppletContext().showDocument(bareUnitURL,"vhlk"); } } else { URL logoURL = new URL(graphObj.KHSRLogopage); graphObj.getAppletContext().showDocument(logoURL,"vhlk"); } } catch (MalformedURLException e) { troubleShooting("MalformedURLException beim öffnen von " + soleDaughter.theURL); } } return true; } public void start() { relaxer = new Thread(this); relaxer.start(); // Get handle to browser frame; this is a little tricky, I must admit; explanation at http://smapp.gis.de Component ParentComponent; ParentComponent = getParent(); while (ParentComponent != null && !(ParentComponent instanceof Frame)) { ParentComponent = ParentComponent.getParent(); } BrowserFrame_ = (Frame) ParentComponent; } public void stop() { relaxer.stop(); } public void troubleShooting(String whatsWrong) { emergencyStringBuf.setLength(0); emergencyStringBuf.append(whatsWrong); fatalError = true; repaint(); } } public class KHSClient extends Applet { GraphPanel panel; StringBuffer TraditionalAccessToEntryPage = new StringBuffer(); StringBuffer StructureOnlyUnitsCallThisURL = new StringBuffer(); String KHSRLogopage = new String("http://pc5939.psychologie.uni-regensburg.de:8008/realizeKHSClient/KHSRLogo.htm"); int CurrentDeepness = 0; public void init() { Vector daughtersNames = new Vector(10,5); setLayout(new BorderLayout()); panel = new GraphPanel(this); add("Center", panel); //Panel p = new Panel(); This is how you would add panels //add("South", p); MediaTracker mediaTracker = new MediaTracker(this); URL codeBase = getCodeBase(); panel.contentSymbol = getImage(codeBase,"ContentSymbol.gif"); mediaTracker.addImage(panel.contentSymbol,0); panel.upperSymbol = getImage(codeBase,"UpSymbol.gif"); mediaTracker.addImage(panel.upperSymbol,0); panel.downerSymbol = getImage(codeBase,"DownSymbol.gif"); mediaTracker.addImage(panel.downerSymbol,0); try { mediaTracker.waitForAll(); } catch (InterruptedException e) { panel.troubleShooting("Media tracker exception trying to load symbols"); } panel.leveledColors[0] = new Color(135,255,255); panel.leveledColors[1] = new Color(150,240,255); panel.leveledColors[2] = new Color(165,225,255); panel.leveledColors[3] = new Color(180,210,255); panel.leveledColors[4] = new Color(195,195,255); panel.leveledColors[5] = new Color(210,180,255); panel.leveledColors[6] = new Color(225,165,255); panel.leveledColors[7] = new Color(240,150,255); panel.leveledColors[8] = new Color(255,135,255); String serverandport = getParameter("serverandport"); String hypertext = getParameter("hypertext"); String initialnode = getParameter("initialnode"); String initiallabel = getParameter("initiallabel"); String structuremsg = getParameter("structuremsg"); try { TraditionalAccessToEntryPage.append(serverandport + "/khs/access/" + hypertext + "/unit/" + initialnode); StructureOnlyUnitsCallThisURL.append(serverandport + "/realizeKHSClient/" + structuremsg); URL iniURL = new URL(serverandport + "/walterskhs/meta/" + hypertext + "/unit/" + initialnode); BufferedInputStream in = new BufferedInputStream(iniURL.openStream()); eatUninterestingStuff(in); // Consume stuff before hierarchy info getMotherOfMine(in); // Consume hierarchy info panel.theMother.label = "?"; // Because we are doing the initial request panel.theMother.theURL = "http://pc5939.psychologie.uni-regensburg.de:8008/realizeKHSClient/KHSQuestionmark.htm"; // Via FileResponder panel.theMother.hasContent = true; // The text of KHSQuestionmark.htm // This would work for multiple daughters, too if (daughtersNames.contains(initiallabel)) { // Initially impossible } else // Make new daughter { Daughter daughter = new Daughter(); daughter.label = initiallabel; daughter.theURL = TraditionalAccessToEntryPage.toString(); daughter.hasContent = true; // Open door to traditinal KHS daughter.distance2root = CurrentDeepness; daughter.selected = true; panel.theMother.Daughters.addElement(daughter); daughtersNames.addElement(daughter.label); } // This is outlined to work with multiple daugthers (not supported at the moment) for (Enumeration enum = panel.theMother.Daughters.elements();enum.hasMoreElements();) { Daughter daughterTmp = (Daughter) enum.nextElement(); if (0 == daughterTmp.label.compareTo(initiallabel)) // We got her { // Actually, there is only one daughter, so I simlpy append all granddaughters here while (true) { String aChildOfMine = new String(getNextChildOfMine(in)); int cmp = aChildOfMine.compareTo("UNDEFINED"); if (cmp == 0) // No more granddaughters { break; } else { GrandDaughter grandDaughter = new GrandDaughter(); grandDaughter.label = labelSubString(aChildOfMine); grandDaughter.theURL = access2meta(urlSubString(aChildOfMine)); grandDaughter.hasContent = infoSubStringIsTerminal(aChildOfMine); grandDaughter.isTerminal = infoSubStringIsTerminal(aChildOfMine); daughterTmp.GrandDaughters.addElement(grandDaughter); } } } } in.close(); } catch (MalformedURLException e) { panel.troubleShooting("MalformedURLException in method init()"); } catch (IOException e) { panel.troubleShooting("IOException in method init()"); } catch (SecurityException e) { panel.troubleShooting("SecurityException in method init()"); } setBackground(panel.backgroundColor); panel.repaint(); } public void reInit(URL newDaughterURL,String dauLabel,String dauURL,boolean dauHasContent) { Vector daughtersNames = new Vector(10,5); // Flush old context information for (Enumeration enum = panel.theMother.Daughters.elements();enum.hasMoreElements();) { Daughter daughter = (Daughter) enum.nextElement(); daughter.GrandDaughters.removeAllElements(); } panel.theMother.Daughters.removeAllElements(); // Parse context information from URL (new daughter node) try { BufferedInputStream in = new BufferedInputStream(newDaughterURL.openStream()); eatUninterestingStuff(in); // Read mother String motherOfMine = getMotherOfMine(in);; panel.theMother.label = labelSubString(motherOfMine); panel.theMother.theURL = access2meta(urlSubString(motherOfMine)); panel.theMother.hasContent = false; panel.theMother.isTerminal = infoSubStringIsTerminal(motherOfMine); panel.theMother.selected = false; if (panel.theMother.label.compareTo(getParameter("initiallabel")) == 0) // Special case { panel.theMother.hasContent = true; } if (dauLabel.compareTo(getParameter("initiallabel")) == 0) // It's the root doc, so... { panel.theMother.label = "?"; panel.theMother.theURL = "http://pc5939.psychologie.uni-regensburg.de:8008/realizeKHSClient/KHSQuestionmark.htm"; panel.theMother.hasContent = true; panel.theMother.isTerminal = false; panel.theMother.selected = false; } // Configure daughter; this is outlined to work with multiple daugthers (not supported at the moment) if (daughtersNames.contains(dauLabel)) { // Do nothing, daughters must not have identical names } else // Make new daughter { Daughter daughter = new Daughter(); daughter.label = new String(dauLabel); daughter.theURL = new String(dauURL); daughter.hasContent = dauHasContent; daughter.isTerminal = false; daughter.distance2root = CurrentDeepness; daughter.selected = true; panel.theMother.Daughters.addElement(daughter); // Remember her name daughtersNames.addElement(daughter.label); } // This is outlined to work with multiple daugthers (not supported at the moment) for (Enumeration enum = panel.theMother.Daughters.elements();enum.hasMoreElements();) { Daughter daughterTmp = (Daughter) enum.nextElement(); if (0 == daughterTmp.label.compareTo(dauLabel)) // We got her { // Actually, there is only one daughter, so I simlpy append all granddaughters here while (true) { String aChildOfMine = new String(getNextChildOfMine(in)); int cmp = aChildOfMine.compareTo("UNDEFINED"); if (cmp == 0) // No more granddaughters { break; } else { GrandDaughter grandDaughter = new GrandDaughter(); grandDaughter.label = labelSubString(aChildOfMine); grandDaughter.theURL = access2meta(urlSubString(aChildOfMine)); grandDaughter.hasContent = infoSubStringIsTerminal(aChildOfMine); grandDaughter.isTerminal = infoSubStringIsTerminal(aChildOfMine); daughterTmp.GrandDaughters.addElement(grandDaughter); } } } } in.close(); panel.newLayout = true; panel.repaint(); } catch (MalformedURLException e) { panel.troubleShooting("MalformedURLException in method reInit()"); } catch (IOException e) { panel.troubleShooting("IOException in method reInit()"); } catch (SecurityException e) { panel.troubleShooting("SecurityException in method reInit()"); } } private void eatUninterestingStuff(InputStream in) { int b; char sniff4it[] = {'I','D','=','"','H','I','E','R','A','R','C','H','Y','"','>'}; int cntr = -1; boolean harkening = false; try { while ((b = in.read()) != -1) { if (!harkening && (char)b == sniff4it[0]) { cntr = 0; harkening = true; continue; } if (harkening) { cntr++; if ((char)b == sniff4it[cntr]) { if ((char)b == '>') { return; } else { continue; } } else { cntr = -1; harkening = false; } } } } catch (Exception e) { panel.troubleShooting("Error: eatUninterestingStuff read failure"); } } private String getMotherOfMine(InputStream in) { int b; StringBuffer hierBuf = new StringBuffer(512); char sniff4it[] = {'/','D','I','V','>'}; int cntr = -1; boolean harkening = false; try { while ((b = in.read()) != -1) { hierBuf.append((char)b); if (!harkening && (char)b == sniff4it[0]) { cntr = 0; harkening = true; continue; } if (harkening) { cntr++; if ((char)b == sniff4it[cntr]) { if ((char)b == '>') { break; } else { continue; } } else { cntr = -1; harkening = false; } } } } catch (IOException e) { panel.troubleShooting("Error: getMotherUnitLabel read failure"); } // hierBuf now contains the hierarchy information, address and name of parent String debug = new String(hierBuf.toString()); StringTokenizer st = new StringTokenizer(hierBuf.toString()); StringBuffer garbage = new StringBuffer(); StringBuffer address = new StringBuffer(); StringBuffer name = new StringBuffer(); while (true) { garbage.setLength(0); garbage.append(st.nextToken("\"")); address.setLength(0); address.append(st.nextToken("\"")); garbage.setLength(0); garbage.append(st.nextToken("\"")); garbage.setLength(0); garbage.append(st.nextToken("\"")); name.setLength(0); name.append(st.nextToken("<")); if (st.countTokens() <= 2) break; } String pureName = (name.toString()).substring(2); return address.toString() + "|" + pureName +"$" + "STRUCTUREONLY"; } private String getNextChildOfMine(BufferedInputStream in) { StringBuffer candidate = new StringBuffer(); int b; boolean harkening = false; boolean somethingFound = false; char sniff4it[] = {'<','/','A','>'}; int cntr = -1; try { while ((b = in.read()) != -1) { candidate.append((char)b); if (!harkening && (char)b == sniff4it[0]) { cntr = 0; harkening = true; continue; } if (harkening) { cntr++; if ((char)b == sniff4it[cntr]) { if ((char)b == '>') { somethingFound = true; break; } else { continue; } } else { cntr = -1; harkening = false; } } } } catch (Exception e) { panel.troubleShooting("Error: getNextChildOfMine read failure"); } if (somethingFound == false) // Past last node { return "UNDEFINED"; } else // Figure out child description from candidate { // Cut away leading stuff before http://..... String strStep1 = candidate.toString(); int idx = strStep1.indexOf("http://"); String strStep2 = strStep1.substring(idx); int idxHTTPend = strStep2.indexOf("\""); StringBuffer resultURL = new StringBuffer(strStep2.substring(0,idxHTTPend)); // Got URL int idxNameBegin = strStep2.indexOf("return true\">") + 13; int idxNameEnd = strStep2.indexOf(""); String resultName = strStep2.substring(idxNameBegin,idxNameEnd); // Got Name StringBuffer resultInfo = new StringBuffer(); StringBuffer pureName = new StringBuffer(); int idxUglyTail; if ((idxUglyTail = (resultName.toString()).indexOf("...")) == -1) { pureName.append(resultName); resultInfo.append("TERMINALTEXT"); } else { pureName.append(resultName.substring(0,idxUglyTail)); resultInfo.append("STRUCTUREONLY"); } // Got Unit Type return resultURL + "|" + pureName + "$" + resultInfo; } } private String labelSubString(String combinedUnitDescription) { int idxBegin = combinedUnitDescription.indexOf("|"); int idxEnd = combinedUnitDescription.indexOf("$"); // Special characters of the german language must be deHTMLed String replacePatterns[] = {"Ä","ä","Ö","ö","Ü","ü"}; char replaceChars[] = {'Ä','ä','Ö','ö','Ü','ü'}; String raw = new String(combinedUnitDescription.substring(idxBegin+1,idxEnd)); StringBuffer parseMe = new StringBuffer(raw); StringBuffer cooked = new StringBuffer(); int idxb; int idxe; for (int i = 0;i < 6;i++) { idxb = idxe = 0; cooked.setLength(0); while ((idxe = (parseMe.toString()).indexOf(replacePatterns[i])) != -1) { cooked.append((parseMe.toString()).substring(idxb,idxe)); cooked.append(replaceChars[i]); idxb = idxe + 6; String nextPart = new String((parseMe.toString()).substring(idxb)); parseMe.setLength(0); parseMe.append(nextPart); } String parseMeTail = new String(parseMe.toString()); parseMe.setLength(0); parseMe.append(cooked.toString()); parseMe.append(parseMeTail); } return parseMe.toString(); } private String urlSubString(String combinedUnitDescription) { int idxEnd = combinedUnitDescription.indexOf("|"); return combinedUnitDescription.substring(0,idxEnd); } private boolean infoSubStringIsTerminal(String combinedUnitDescription) { if (-1 != combinedUnitDescription.indexOf("$TERMINALTEXT")) return true; else return false; } private String access2meta(String access) { int cut = access.indexOf("?sessionKey"); String prepared = access.substring(0,cut); int idxBeginChangePart = prepared.indexOf("/khs/access/"); int idxEndChangePart = idxBeginChangePart + 12; String serverandport = prepared.substring(0,idxBeginChangePart); String unchangedTail = prepared.substring(idxEndChangePart); return serverandport + "/walterskhs/meta/" + unchangedTail; } public String meta2bare(String meta) { int idxBeginChangePart = meta.indexOf("/walterskhs/meta/"); if (idxBeginChangePart == -1) { return meta; // Leave alone special URLs } int idxEndChangePart = idxBeginChangePart + 16; String serverandport = meta.substring(0,idxBeginChangePart); String unchangedTail = meta.substring(idxEndChangePart); return serverandport + "/walterskhs/access/" + unchangedTail; } public void start() { panel.start(); } public void stop() { panel.stop(); } }