/*******************************************************************************
* Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Exadel, Inc. and Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.jsf.model.helpers;
import java.util.*;
import org.jboss.tools.common.model.*;
import org.jboss.tools.common.model.util.XModelObjectUtil;
import org.jboss.tools.jsf.JSFPreference;
import org.jboss.tools.jsf.model.*;
import org.jboss.tools.jsf.model.helpers.autolayout.JSFItems;
import org.jboss.tools.jsf.model.impl.NavigationRuleObjectImpl;
import org.jboss.tools.jst.web.model.ReferenceObject;
import org.jboss.tools.jst.web.model.helpers.autolayout.AutoLayout;
public class JSFProcessHelper implements JSFConstants {
private XModelObject process;
private static XModelObject TEMPLATE;
private XModelObject config;
private Map<String,XModelObject> groups = new HashMap<String,XModelObject>();
private Map<String,XModelObject> targets = new HashMap<String,XModelObject>();
private XModelObject[] rules = new XModelObject[0];
public JSFProcessHelper(XModelObject process) {
this.process = process;
}
private XModelObject getTemplate() {
if(TEMPLATE == null && process != null) {
TEMPLATE = XModelFactory.getDefaultInstance().createModelObject(ENT_PROCESS_GROUP, null);
}
return TEMPLATE;
}
public static JSFProcessHelper getHelper(XModelObject process) {
return ((FacesProcessImpl)process).getHelper();
}
private synchronized void reset() {
groups.clear();
targets.clear();
this.config = process.getParent();
}
public void restoreRefs() {
((FacesProcessImpl)process).setReference(process.getParent());
}
Set<Object> updateLocks = new HashSet<Object>();
public boolean isUpdateLocked() {
return updateLocks.size() > 0;
}
public void addUpdateLock(Object lock) {
updateLocks.add(lock);
}
public void removeUpdateLock(Object lock) {
updateLocks.remove(lock);
}
public void updateProcess() {
if(isUpdateLocked()) return;
addUpdateLock(this);
try {
updateProcess0();
} finally {
removeUpdateLock(this);
}
}
private void updateProcess0() {
reset();
rules = config.getChildByPath(FOLDER_NAVIGATION_RULES).getChildren();
for (int i = 0; i < rules.length; i++) {
String fvi = rules[i].getAttributeValue(ATT_FROM_VIEW_ID);
if(fvi == null) continue;
String pp = getRuleIdentity(rules[i]);
XModelObject g = findOrCreateGroup(fvi, pp);
groups.put(pp, g);
XModelObject[] cs = rules[i].getChildren();
for (int j = 0; j < cs.length; j++) {
String tvi = cs[j].getAttributeValue(ATT_TO_VIEW_ID);
if(tvi == null) continue;
String ppt = NavigationRuleObjectImpl.toNavigationRulePathPart(tvi);
targets.put(ppt, getTemplate());
}
}
for(String s: groups.keySet()) targets.remove(s);
removeObsoleteGroups();
createPageGroups();
updateGroups();
updatePages();
}
private String getRuleIdentity(XModelObject rule) {
String fvi = rule.getAttributeValue(ATT_FROM_VIEW_ID);
String pp = NavigationRuleObjectImpl.toNavigationRulePathPart(fvi);
if(!isPattern(fvi)) return pp;
String index = rule.getAttributeValue("index"); //$NON-NLS-1$
if(index.startsWith("-") || index.equals("0")) return pp; //$NON-NLS-1$ //$NON-NLS-2$
return pp + ":" + index; //$NON-NLS-1$
}
public XModelObject findOrCreateGroup(String path, String pp) {
if(pp == null) pp = NavigationRuleObjectImpl.toNavigationRulePathPart(path);
XModelObject g = process.getChildByPath(pp);
if(g == null) {
g = process.getModel().createModelObject(ENT_PROCESS_GROUP, null);
g.setAttributeValue(ATT_NAME, pp);
g.setAttributeValue(ATT_PATH, path);
process.addChild(g);
}
return g;
}
private void removeObsoleteGroups() {
boolean q = "yes".equals(JSFPreference.DO_NOT_CREATE_EMPTY_RULE.getValue()); //$NON-NLS-1$
XModelObject[] ps = process.getChildren(ENT_PROCESS_GROUP);
for (int i = 0; i < ps.length; i++) {
String path = ps[i].getPathPart();
if(!groups.containsKey(path) && !targets.containsKey(path)) {
if(q && "true".equals(ps[i].getAttributeValue("persistent"))) { //$NON-NLS-1$ //$NON-NLS-2$
groups.put(path, ps[i]);
} else {
ps[i].removeFromParent();
}
}
}
}
private void createPageGroups() {
String[] paths = (String[])targets.keySet().toArray(new String[0]);
for (int i = 0; i < paths.length; i++) {
String fvi = NavigationRuleObjectImpl.toFromViewId(paths[i]);
XModelObject g = findOrCreateGroup(fvi, paths[i]);
targets.put(paths[i], g);
}
}
private void updateGroups() {
ReferenceGroupImpl[] gs = (ReferenceGroupImpl[])groups.values().toArray(new ReferenceGroupImpl[0]);
for (int i = 0; i < gs.length; i++) {
setGroupReferences(rules, gs[i]);
updateGroup(gs[i]);
}
gs = (ReferenceGroupImpl[])targets.values().toArray(new ReferenceGroupImpl[0]);
for (int i = 0; i < gs.length; i++) {
gs[i].setReference(new XModelObject[0]);
updateGroup(gs[i]);
}
}
private void setGroupReferences(XModelObject[] rules, ReferenceGroupImpl group) {
String path = group.getPathPart();
ArrayList<XModelObject> list = null;
for (int i = 0; i < rules.length; i++) {
String pp = getRuleIdentity(rules[i]);
if(!path.equals(pp)) continue;
if(list == null) list = new ArrayList<XModelObject>();
list.add(rules[i]);
}
XModelObject[] rs = (list == null) ? new XModelObject[0] : list.toArray(new XModelObject[list.size()]);
group.setReference(rs);
}
private void updateGroup(ReferenceGroupImpl group) {
if(group.isUpToDate()) return;
group.notifyUpdate();
XModelObject[] rs = group.getReferences();
XModelObject[] is = group.getChildren(ENT_PROCESS_ITEM);
for (int i = 0; i < rs.length; i++) {
XModelObject item = null;
if(i < is.length) {
item = is[i];
} else {
item = createItem(group, rs[i]);
}
ReferenceObjectImpl r = (ReferenceObjectImpl)item;
r.setReference(rs[i]);
updateItem(r);
}
for (int i = rs.length; i < is.length; i++) is[i].removeFromParent();
}
private XModelObject createItem(XModelObject group, XModelObject rule) {
XModelObject item = group.getModel().createModelObject(ENT_PROCESS_ITEM, null);
item.setAttributeValue(ATT_ID, rule.getPathPart());
item.setAttributeValue(ATT_PATH, rule.getAttributeValue(ATT_FROM_VIEW_ID));
String name = XModelObjectUtil.createNewChildName("item", group); //$NON-NLS-1$
item.setAttributeValue(ATT_NAME, name);
group.addChild(item);
return item;
}
private void updateItem(ReferenceObjectImpl item) {
if(item.isUpToDate()) return;
item.notifyUpdate();
XModelObject rule = item.getReference();
item.setAttributeValue(ATT_ID, rule.getPathPart());
item.setAttributeValue(ATT_PATH, rule.getAttributeValue(ATT_FROM_VIEW_ID));
updateOutputs(item);
}
private void updateOutputs(ReferenceObjectImpl item) {
XModelObject rule = item.getReference();
XModelObject[] cs = rule.getChildren();
XModelObject[] os = item.getChildren();
if(isOutputOrderUpToDate(cs, os)) {
updateOutputs_1(item, cs, os);
} else {
updateOutputs_2(item, cs, 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++) {
outputs[i].removeFromParent();
map.put(outputs[i].getAttributeValue(ATT_ID), outputs[i]);
}
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].getPathPart();
String id = outputs[i].getAttributeValue(ATT_ID);
if(!pp.equals(id)) return false;
}
return true;
}
private XModelObject createOutput(XModelObject item, XModelObject rulecase) {
XModelObject output = item.getModel().createModelObject(ENT_PROCESS_ITEM_OUTPUT, null);
output.setAttributeValue(ATT_ID, rulecase.getPathPart());
output.setAttributeValue(ATT_PATH, rulecase.getAttributeValue(ATT_TO_VIEW_ID));
String name = XModelObjectUtil.createNewChildName("output", item); //$NON-NLS-1$
output.setAttributeValue(ATT_NAME, name);
item.addChild(output);
return output;
}
private void updateOutput(ReferenceObjectImpl output) {
if(output.isUpToDate()) return;
output.notifyUpdate();
XModelObject rulecase = output.getReference();
output.setAttributeValue(ATT_ID, rulecase.getPathPart());
String path = rulecase.getAttributeValue(ATT_TO_VIEW_ID);
output.setAttributeValue(ATT_PATH, path);
String title = JSFProcessStructureHelper.createItemOutputPresentation(rulecase);
output.setAttributeValue("title", title); //$NON-NLS-1$
XModelObject g = findGroupByPath(path);
String target = (g == null) ? "" : g.getPathPart(); //$NON-NLS-1$
output.setAttributeValue(ATT_TARGET, target);
}
private XModelObject findGroupByPath(String path) {
return getPage(path);
}
public void autolayout() {
AutoLayout auto = new AutoLayout();
auto.setItems(new JSFItems());
auto.setProcess(process);
}
public XModelObject getPage(String path) {
path = NavigationRuleObjectImpl.toNavigationRulePathPart(path);
XModelObject g = (XModelObject)groups.get(path);
if(g == null) g = (XModelObject)targets.get(path);
return g;
}
public void updatePages() {
JSFPageUpdateManager pu = JSFPageUpdateManager.getInstance(process.getModel());
pu.lock();
XModelObject[] items = process.getChildren();
for (int i = 0; i < items.length; i++) pu.updatePage(this, items[i]);
pu.unlock();
}
public static boolean isPattern(String path) {
return path != null && (path.length() == 0 || path.indexOf('*') >= 0);
}
}