package org.jboss.tools.seam.pages.xml.model.helpers; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.jboss.tools.common.model.XModelObject; import org.jboss.tools.common.model.util.XModelObjectUtil; import org.jboss.tools.jst.web.model.ReferenceObject; import org.jboss.tools.jst.web.model.helpers.autolayout.AutoLayout; import org.jboss.tools.seam.pages.xml.model.SeamPagesConstants; import org.jboss.tools.seam.pages.xml.model.helpers.autolayout.SeamPagesItems; import org.jboss.tools.seam.pages.xml.model.impl.ReferenceObjectImpl; import org.jboss.tools.seam.pages.xml.model.impl.SeamPagesDiagramImpl; public class SeamPagesDiagramHelper implements SeamPagesConstants { private XModelObject diagram; private static XModelObject TEMPLATE; private XModelObject config; private Map<String,XModelObject> pageItems = new HashMap<String,XModelObject>(); private Set<String> pageViewIds = new HashSet<String>(); private Map<String,XModelObject> exceptionItems = new HashMap<String,XModelObject>(); private Map<String,XModelObject> targets = new HashMap<String,XModelObject>(); private Set<String> changedTargets = new HashSet<String>(); public SeamPagesDiagramHelper(XModelObject diagram) { this.diagram = diagram; } public static SeamPagesDiagramHelper getHelper(XModelObject diagram) { return ((SeamPagesDiagramImpl)diagram).getHelper(); } private synchronized void reset() { pageItems.clear(); pageViewIds.clear(); exceptionItems.clear(); targets.clear(); changedTargets.clear(); this.config = diagram.getParent(); } public void restoreRefs() { ((SeamPagesDiagramImpl)diagram).setReference(diagram.getParent()); } Set<Object> updateLocks = new HashSet<Object>(); public boolean isUpdateLocked() { return !updateLocks.isEmpty(); } public void addUpdateLock(Object lock) { updateLocks.add(lock); } public void removeUpdateLock(Object lock) { updateLocks.remove(lock); } public void updateDiagram() { if(isUpdateLocked()) return; addUpdateLock(this); try { updateDiagram0(); } finally { removeUpdateLock(this); } } private void updateDiagram0() { reset(); XModelObject[] sourcePages = config.getChildByPath(FOLDER_PAGES).getChildren(); XModelObject[] cs = diagram.getChildren(); Map<XModelObject,XModelObject> old = new HashMap<XModelObject, XModelObject>(); for (int i = 0; i < cs.length; i++) { if(cs[i] instanceof ReferenceObject) { XModelObject k = ((ReferenceObject)cs[i]).getReference(); if(k != null) old.put(k, cs[i]); } } for (int i = 0; i < sourcePages.length; i++) { String view = sourcePages[i].getAttributeValue(ATTR_VIEW_ID); if(view == null) continue; String pp = toNavigationRulePathPart(view); XModelObject og = old.get(sourcePages[i]); if(og != null) { String opp = og.getPathPart(); if(!pp.equals(opp)) { XModelObject og1 = diagram.getChildByPath(pp); if(og1 != null) { og1.removeFromParent(); changedTargets.add(toNavigationRulePathPart(og1.getAttributeValue(ATTR_PATH))); } pageItems.remove(opp); og.setAttributeValue(ATTR_NAME, pp); } } XModelObject g = og != null ? og : findOrCreateItem(view, pp, TYPE_PAGE); ((ReferenceObjectImpl)g).setReference(sourcePages[i]); pageItems.put(pp, g); pageViewIds.add(view); XModelObject[] ns = sourcePages[i].getChildren(); for (int j = 0; j < ns.length; j++) { String entity = ns[j].getModelEntity().getName(); if(!entity.startsWith(ENT_NAVIGATION)) continue; if(entity.startsWith(ENT_NAVIGATION_RULE)) { addTarget(ns[j], true); if(!entity.endsWith(SUFF_21) && !entity.endsWith(SUFF_22)) { continue; } } XModelObject[] rs = ns[j].getChildren(); for (int k = 0; k < rs.length; k++) { addTarget(rs[k], true); } } } XModelObject[] sourceExceptions = config.getChildByPath(FOLDER_EXCEPTIONS).getChildren(); for (int i = 0; i < sourceExceptions.length; i++) { String code = sourceExceptions[i].getAttributeValue("class"); String pp = "exception:" + code; XModelObject og = old.get(sourceExceptions[i]); if(og != null) { String opp = og.getPathPart(); if(!pp.equals(opp)) { exceptionItems.remove(opp); og.setAttributeValue(ATTR_NAME, pp); ((ReferenceObjectImpl)og).setReference(null); } } XModelObject g = og != null ? og : findOrCreateItem(code, pp, TYPE_EXCEPTION); ((ReferenceObjectImpl)g).setReference(sourceExceptions[i]); exceptionItems.put(pp, g); addTarget(sourceExceptions[i], false); } Iterator<String> it = pageItems.keySet().iterator(); while(it.hasNext()) targets.remove(it.next()); removeObsoleteExceptionItems(); createPageItems(); removeObsoletePageItems(); updatePageItems(); updateExceptionItems(); updatePages(); } private void addTarget(XModelObject rule, boolean addEmpty) { XModelObject target = rule.getChildByPath("target"); if(target == null) return; String tvi = target.getAttributeValue(ATTR_VIEW_ID); if(tvi == null) return; if(!addEmpty && tvi.length() == 0) return; targets.put(toNavigationRulePathPart(tvi), getTemplate()); } private XModelObject getTemplate() { if(TEMPLATE == null && diagram != null) { TEMPLATE = diagram.getModel().createModelObject(ENT_DIAGRAM_ITEM, null); } return TEMPLATE; } public XModelObject findOrCreateItem(String path, String pp, String type) { if(pp == null) pp = toNavigationRulePathPart(path); XModelObject g = diagram.getChildByPath(pp); if(g == null) { g = diagram.getModel().createModelObject(ENT_DIAGRAM_ITEM, null); g.setAttributeValue(ATTR_NAME, pp); g.setAttributeValue(ATTR_PATH, path); g.setAttributeValue(ATTR_TYPE, type); diagram.addChild(g); changedTargets.add(pp); } return g; } private void removeObsoletePageItems() { XModelObject[] ps = diagram.getChildren(ENT_DIAGRAM_ITEM); for (int i = 0; i < ps.length; i++) { String path = ps[i].getPathPart(); String type = ps[i].getAttributeValue(ATTR_TYPE); if(!TYPE_PAGE.equals(type)) continue; if(!pageItems.containsKey(path) && targets.get(path) != ps[i]) { ps[i].removeFromParent(); } } } private void removeObsoleteExceptionItems() { XModelObject[] ps = diagram.getChildren(ENT_DIAGRAM_ITEM); for (int i = 0; i < ps.length; i++) { String path = ps[i].getPathPart(); String type = ps[i].getAttributeValue(ATTR_TYPE); if(!TYPE_EXCEPTION.equals(type)) continue; if(!exceptionItems.containsKey(path)) { ps[i].removeFromParent(); } } } private void createPageItems() { String[] paths = (String[])targets.keySet().toArray(new String[0]); for (int i = 0; i < paths.length; i++) { String fvi = toFromViewId(paths[i]); // fvi = findBestMatch(fvi); XModelObject g = findOrCreateItem(fvi, toNavigationRulePathPart(fvi), TYPE_PAGE); targets.put(paths[i], g); } } private void updatePageItems() { ReferenceObjectImpl[] gs = (ReferenceObjectImpl[])pageItems.values().toArray(new ReferenceObjectImpl[0]); for (int i = 0; i < gs.length; i++) { updatePageItem(gs[i]); } gs = (ReferenceObjectImpl[])targets.values().toArray(new ReferenceObjectImpl[0]); for (int i = 0; i < gs.length; i++) { long ts = gs[i].getTimeStamp(); boolean hadReference = gs[i].getReference() != null; gs[i].setReference(null); gs[i].setAttributeValue("params", ""); XModelObject[] os = gs[i].getChildren(ENT_DIAGRAM_ITEM_OUTPUT); for (int j = 0; j < os.length; j++) { if(SUBTYPE_CUSTOM.equals(os[j].getAttributeValue(ATTR_SUBTYPE))) { continue; } gs[i].removeChild(os[j]); } updatePageItem(gs[i]); if(hadReference && ts == gs[i].getTimeStamp()) { gs[i].fireReferenceChanged(); } } } private void updateExceptionItems() { ReferenceObjectImpl[] gs = (ReferenceObjectImpl[])exceptionItems.values().toArray(new ReferenceObjectImpl[0]); for (int i = 0; i < gs.length; i++) { updateExceptionItem(gs[i]); } } private void updatePageItem(ReferenceObjectImpl item) { if(item.getReference() == null) { updateUndeclaredPageItem(item); return; } // if(item.isUpToDate()) return; boolean iud = item.isUpToDate(); long ts = item.getTimeStamp(); item.notifyUpdate(); XModelObject sourcePage = item.getReference(); item.setAttributeValue(ATTR_ID, sourcePage.getPathPart()); item.setAttributeValue(ATTR_PATH, sourcePage.getAttributeValue(ATTR_VIEW_ID)); String[][] params = SeamPagesDiagramStructureHelper.getInstance().getParams(item); StringBuffer sb = new StringBuffer(); for (int i = 0; i < params.length; i++) { sb.append(params[i][0]).append('=').append(params[i][1]).append(';'); } item.setAttributeValue("params", sb.toString()); XModelObject[] cs = getPageTargets(sourcePage); updateOutputs(item, cs); if(!iud && ts == item.getTimeStamp()) { item.fireReferenceChanged(); } } private void updateUndeclaredPageItem(ReferenceObjectImpl item) { //update virtual link to findBestMatch(path) element XModelObject[] os = item.getChildren(); String path = item.getAttributeValue(ATTR_PATH); String fvi = findBestMatch(path); XModelObject g = getPage(fvi); if(g != null && item != g) { XModelObject output = item.getModel().createModelObject(ENT_DIAGRAM_ITEM_OUTPUT, null); output.setAttributeValue(ATTR_ID, fvi); output.setAttributeValue(ATTR_PATH, fvi); String name = XModelObjectUtil.createNewChildName("output", item); output.setAttributeValue(ATTR_NAME, name); ReferenceObjectImpl r = (ReferenceObjectImpl)output; r.setReference(null); String target = (g == null) ? "" : g.getPathPart(); output.setAttributeValue(ATTR_TARGET, target); item.addChild(output); } } private XModelObject[] getPageTargets(XModelObject o) { XModelObject[] ns = o.getChildren(); List<XModelObject> result = null; for (int i = 0; i < ns.length; i++) { String entity = ns[i].getModelEntity().getName(); if(!entity.startsWith(ENT_NAVIGATION)) continue; if(entity.startsWith(ENT_NAVIGATION_RULE)) { XModelObject t = getTargetChild(ns[i]); if(t != null) { if(result == null) result = new ArrayList<XModelObject>(); result.add(t); } if(!entity.endsWith(SUFF_21) && !entity.endsWith(SUFF_22)) { continue; } } XModelObject[] rs = ns[i].getChildren(); for (int k = 0; k < rs.length; k++) { XModelObject t = getTargetChild(rs[k]); if (t != null) { if (result == null) result = new ArrayList<XModelObject>(); result.add(t); } } } return result == null ? new XModelObject[0] : result.toArray(new XModelObject[0]); } private void updateExceptionItem(ReferenceObjectImpl item) { if(item.isUpToDate()) return; item.notifyUpdate(); XModelObject exc = item.getReference(); item.setAttributeValue(ATTR_ID, exc.getPathPart()); item.setAttributeValue(ATTR_PATH, exc.getAttributeValue("class")); XModelObject t = getTargetChild(exc); XModelObject[] cs = t == null ? new XModelObject[0] : new XModelObject[]{t}; if(cs.length == 1) { String path = t.getAttributeValue(ATTR_VIEW_ID); if(path == null || path.length() == 0) { cs = new XModelObject[0]; } } updateOutputs(item, cs); } private XModelObject getTargetChild(XModelObject o) { XModelObject t = o.getChildByPath("target"); if(t == null || t.getModelEntity().getAttribute(ATTR_VIEW_ID) == null) return null; return t; } private void updateOutputs(ReferenceObjectImpl item, XModelObject[] cases) { XModelObject[] os = item.getChildren(); if(isOutputOrderUpToDate(cases, os)) { updateOutputs_1(item, cases, os); } else { updateOutputs_2(item, cases, os); } } private void updateOutputs_1(ReferenceObjectImpl item, XModelObject[] cases, XModelObject[] outputs) { int c = 0; for (int i = 0; i < cases.length; i++) { XModelObject output = null; if(c < outputs.length) { output = outputs[c]; } else { output = createOutput(item, cases[i]); } ReferenceObjectImpl r = (ReferenceObjectImpl)output; r.setReference(cases[i]); updateOutput(r); ++c; } for (int i = c; i < outputs.length; i++) outputs[i].removeFromParent(); } private void updateOutputs_2(ReferenceObjectImpl item, XModelObject[] cases, XModelObject[] outputs) { Map<String,XModelObject> map = new HashMap<String,XModelObject>(); for (int i = 0; i < outputs.length; i++) { XModelObject output = outputs[i]; output.removeFromParent(); map.put(output.getAttributeValue(ATTR_ID), output); } for (int i = 0; i < cases.length; i++) { XModelObject output = map.get(cases[i].getPathPart()); if(output == null) { output = createOutput(item, cases[i]); } else { item.addChild(output); } ReferenceObjectImpl r = (ReferenceObjectImpl)output; r.setReference(cases[i]); updateOutput(r); } } private boolean isOutputOrderUpToDate(XModelObject[] cases, XModelObject[] outputs) { for (int i = 0; i < cases.length && i < outputs.length; i++) { ReferenceObject r = (ReferenceObject)outputs[i]; if(r.getReference() == cases[i]) continue; String pp = cases[i].getAttributeValue(ATTR_VIEW_ID); String id = outputs[i].getAttributeValue(ATTR_ID); if(!pp.equals(id)) return false; } return true; } private XModelObject createOutput(XModelObject item, XModelObject rulecase) { XModelObject output = item.getModel().createModelObject(ENT_DIAGRAM_ITEM_OUTPUT, null); output.setAttributeValue(ATTR_ID, rulecase.getAttributeValue(ATTR_VIEW_ID)); output.setAttributeValue(ATTR_PATH, rulecase.getAttributeValue(ATTR_VIEW_ID)); String name = XModelObjectUtil.createNewChildName("output", item); output.setAttributeValue(ATTR_NAME, name); ReferenceObjectImpl r = (ReferenceObjectImpl)output; r.setReference(rulecase); updateOutput(r); item.addChild(output); return output; } private void updateOutput(ReferenceObjectImpl output) { if(output.getReference() == null) return; // if(output.isUpToDate()) return; output.notifyUpdate(); XModelObject rulecase = output.getReference(); output.setAttributeValue(ATTR_ID, rulecase.getAttributeValue(ATTR_VIEW_ID)); String path = rulecase.getAttributeValue(ATTR_VIEW_ID); output.setAttributeValue(ATTR_PATH, path); // String title = SeamPagesDiagramStructureHelper.createItemOutputPresentation(rulecase); // output.setAttributeValue("title", title); // String fvi = findBestMatch(path); XModelObject g = getPage(path); String target = (g == null) ? "" : g.getPathPart(); if(target != null && target.equals(output.getAttributeValue(ATTR_TARGET)) && changedTargets.contains(target)) { output.setAttributeValue(ATTR_TARGET, ""); } output.setAttributeValue(ATTR_TARGET, target); } public void autolayout() { AutoLayout auto = new AutoLayout(); auto.setItems(new SeamPagesItems()); auto.setProcess(diagram); } public XModelObject getPage(String path) { path = toNavigationRulePathPart(path); XModelObject g = (XModelObject)pageItems.get(path); if(g == null) g = (XModelObject)targets.get(path); return g; } public void updatePages() { SeamPagesPageRefUpdateManager pu = SeamPagesPageRefUpdateManager.getInstance(diagram.getModel()); pu.lock(); XModelObject[] items = diagram.getChildren(); for (int i = 0; i < items.length; i++) pu.updatePage(this, items[i]); pu.unlock(); } public String findBestMatch(String viewId) { if(viewId == null || pageViewIds.contains(viewId)) { return viewId; } String best = viewId; int match = 0; for (String v: pageViewIds) { int i = v.indexOf('*'); if(i < 0) continue; String head = v.substring(0, i); String tail = v.substring(i + 1); int m = head.length() + tail.length(); if(m > viewId.length() || m <= match) continue; if(head.length() > 0 && !viewId.startsWith(head)) continue; if(tail.length() > 0 && !viewId.endsWith(tail)) continue; best = v; match = m; } return best; } public static String toNavigationRulePathPart(String path) { return "" + encode(path); } public static String toFromViewId(String pathpart) { if(!pathpart.startsWith("rules:")) return decode(pathpart); pathpart = decode(pathpart.substring(6)); int i = pathpart.lastIndexOf(':'); return (i < 0) ? pathpart : pathpart.substring(0, i); } public static boolean isPattern(String path) { return path != null && (path.length() == 0 || path.indexOf('*') >= 0); } static String encode(String s) { StringBuffer result = new StringBuffer(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if(c == '/') { result.append("#x"); } else if(c == '#') { result.append("##"); } else { result.append(c); } } return result.toString(); } static String decode(String s) { StringBuffer result = new StringBuffer(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if(c == '#') { char c1 = i + 1 < s.length() ? s.charAt(i + 1) : '\0'; if(c1 == 'x') { result.append('/'); i++; } else if(c1 == '#') { result.append("#"); i++; } else { result.append("#"); } } else { result.append(c); } } return result.toString(); } }