/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; 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 java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import com.servoy.base.persistence.constants.IValueListConstants; import com.servoy.base.query.IBaseSQLCondition; import com.servoy.base.util.DataSourceUtilsBase; import com.servoy.j2db.component.ComponentFactory; import com.servoy.j2db.dataprocessing.DBValueList; import com.servoy.j2db.dataprocessing.IFoundSetManagerInternal; import com.servoy.j2db.dataprocessing.IGlobalValueEntry; import com.servoy.j2db.dataprocessing.SQLGenerator; import com.servoy.j2db.persistence.AbstractBase; import com.servoy.j2db.persistence.AbstractPersistFactory; import com.servoy.j2db.persistence.AbstractRepository; import com.servoy.j2db.persistence.AggregateVariable; import com.servoy.j2db.persistence.Bean; import com.servoy.j2db.persistence.ChangeHandler; import com.servoy.j2db.persistence.Column; import com.servoy.j2db.persistence.ColumnInfo; import com.servoy.j2db.persistence.ColumnWrapper; import com.servoy.j2db.persistence.ContentSpec.Element; import com.servoy.j2db.persistence.EnumDataProvider; import com.servoy.j2db.persistence.FlattenedForm; import com.servoy.j2db.persistence.Form; import com.servoy.j2db.persistence.IActiveSolutionHandler; import com.servoy.j2db.persistence.ICloneable; import com.servoy.j2db.persistence.IColumn; import com.servoy.j2db.persistence.IDataProvider; import com.servoy.j2db.persistence.IDataProviderHandler; import com.servoy.j2db.persistence.IDataProviderLookup; import com.servoy.j2db.persistence.IMediaProvider; import com.servoy.j2db.persistence.IPersist; import com.servoy.j2db.persistence.IPersistListener; import com.servoy.j2db.persistence.IPersistVisitor; import com.servoy.j2db.persistence.IRelationProvider; import com.servoy.j2db.persistence.IRepository; import com.servoy.j2db.persistence.IRootObject; import com.servoy.j2db.persistence.IServer; import com.servoy.j2db.persistence.ISupportChilds; import com.servoy.j2db.persistence.ISupportName; import com.servoy.j2db.persistence.ISupportScope; import com.servoy.j2db.persistence.ISupportScriptProviders; import com.servoy.j2db.persistence.ISupportUpdateableName; import com.servoy.j2db.persistence.ITable; import com.servoy.j2db.persistence.Media; import com.servoy.j2db.persistence.Part; import com.servoy.j2db.persistence.Portal; import com.servoy.j2db.persistence.Relation; import com.servoy.j2db.persistence.RepositoryException; import com.servoy.j2db.persistence.RootObjectMetaData; import com.servoy.j2db.persistence.RootObjectReference; import com.servoy.j2db.persistence.RuntimeProperty; import com.servoy.j2db.persistence.ScriptCalculation; import com.servoy.j2db.persistence.ScriptMethod; import com.servoy.j2db.persistence.ScriptNameValidator; import com.servoy.j2db.persistence.ScriptVariable; import com.servoy.j2db.persistence.ServerProxy; import com.servoy.j2db.persistence.SimplePersistFactory; import com.servoy.j2db.persistence.Solution; import com.servoy.j2db.persistence.SolutionMetaData; import com.servoy.j2db.persistence.StaticContentSpecLoader; import com.servoy.j2db.persistence.Style; import com.servoy.j2db.persistence.Table; import com.servoy.j2db.persistence.TableNode; import com.servoy.j2db.persistence.ValueList; import com.servoy.j2db.query.AndCondition; import com.servoy.j2db.query.CompareCondition; import com.servoy.j2db.query.ISQLJoin; import com.servoy.j2db.query.ISQLTableJoin; import com.servoy.j2db.query.ObjectPlaceholderKey; import com.servoy.j2db.query.Placeholder; import com.servoy.j2db.query.QueryColumn; import com.servoy.j2db.query.QueryCompositeJoin; import com.servoy.j2db.query.QueryJoin; import com.servoy.j2db.query.QueryTable; import com.servoy.j2db.server.shared.IFlattenedSolutionDebugListener; import com.servoy.j2db.util.DataSourceUtils; import com.servoy.j2db.util.Debug; import com.servoy.j2db.util.IntHashMap; import com.servoy.j2db.util.Pair; import com.servoy.j2db.util.ScopesUtils; import com.servoy.j2db.util.UUID; import com.servoy.j2db.util.Utils; import com.servoy.j2db.util.keyword.Ident; /** * Runtime object which represents the entire solution, flattened, containing modules, caching stuff * * @author jcompagner,jblok */ public class FlattenedSolution implements IPersistListener, IDataProviderHandler, IRelationProvider, ISupportScriptProviders, IMediaProvider { private static RuntimeProperty<AbstractBase> CLONE_PROPERTY = new RuntimeProperty<AbstractBase>() { }; private volatile SolutionMetaData mainSolutionMetaData; private volatile Solution mainSolution; private volatile Solution[] modules; private volatile FlattenedSolution loginFlattenedSolution; private volatile FlattenedSolution parentFlattenedSolution; private volatile Solution copySolution = null; private volatile ConcurrentMap<Object, Integer> securityAccess; private volatile ConcurrentMap<Table, Map<String, IDataProvider>> allProvidersForTable = null; //table -> Map(dpname,dp) ,runtime var private final ConcurrentMap<String, IDataProvider> globalProviders = new ConcurrentHashMap<String, IDataProvider>(64, 9f, 16); //global -> dp ,runtime var // concurrent caches. private volatile Map<String, Relation> relationCacheByName = null; private volatile Map<String, Map<String, ISupportScope>> scopeCacheByName = null; private volatile Map<String, Form> formCacheByName = null; private volatile IntHashMap<Form> formCacheById; private volatile Map<String, ValueList> valuelistCacheByName = null; private final List<IPersist> removedPersist = Collections.synchronizedList(new ArrayList<IPersist>(3)); private final List<String> deletedStyles = Collections.synchronizedList(new ArrayList<String>(3)); // 2 synchronized on liveForms object. private final Map<String, Set<String>> liveForms = new HashMap<String, Set<String>>(); private final Map<String, ChangedFormData> changedForms = new HashMap<String, ChangedFormData>(); private volatile HashMap<String, Style> all_styles; // concurrent modification exceptions can happen; see comments from Solution->PRE_LOADED_STYLES. private volatile HashMap<String, Style> user_created_styles; // concurrent modification exceptions shouldn't happen with current implementation for this map; no need to sync private volatile SimplePersistFactory persistFactory; private volatile IFlattenedSolutionDebugListener debugListener; private final ConcurrentMap<Form, FlattenedForm[]> flattenedFormCache; private volatile ConcurrentMap<Bean, Object> beanDesignInstances; /** * @param cacheFlattenedForms turn flattened form caching on when flushFlattenedFormCache() will also be called. */ public FlattenedSolution(boolean cacheFlattenedForms) { flattenedFormCache = cacheFlattenedForms ? new ConcurrentHashMap<Form, FlattenedForm[]>() : null; } public FlattenedSolution() { this(false); } /** * @param solution * @param activeSolutionHandler * @throws RemoteException * @throws RepositoryException */ public FlattenedSolution(SolutionMetaData solutionMetaData, IActiveSolutionHandler activeSolutionHandler) throws RepositoryException, RemoteException { this(false); setSolution(solutionMetaData, true, true, activeSolutionHandler); } @SuppressWarnings("unchecked") public <T extends AbstractBase> T createPersistCopy(T persist) { if (mainSolution == null && loginFlattenedSolution != null) { return loginFlattenedSolution.createPersistCopy(persist); } // if a copy is taken or created then it is for modifying it. // so update the last modified time. getSolutionCopy().updateLastModifiedTime(); T copy = (T)getSolutionCopy().getChild(persist.getUUID()); if (copy != null) return copy; if (persist.getRuntimeProperty(CLONE_PROPERTY) != null) { throw new RuntimeException("Persist " + persist + " already a clone"); //$NON-NLS-1$ //$NON-NLS-2$ } copy = (T)persist.clonePersist(); copy.setRuntimeProperty(CLONE_PROPERTY, persist); copySolution.addChild(copy); flush(persist); if (persist instanceof Form) { refreshSuperForms(persist); } return copy; } @SuppressWarnings({ "unchecked", "nls" }) public <T extends AbstractBase> T clonePersist(T persist, String newName, ISupportChilds newParent) { T clone = (T)persist.clonePersist(); if (newParent != null) { newParent.addChild(clone); } // make sure that this persist is not seen as a copy a real persist/form clone.setRuntimeProperty(CLONE_PROPERTY, null); if (clone instanceof ISupportUpdateableName) { try { ((ISupportUpdateableName)clone).updateName(new ScriptNameValidator(this), newName); } catch (Exception e) { if (newParent != null) { newParent.removeChild(clone); } throw new RuntimeException("name '" + newName + "' invalid for the clone of " + ((ISupportName)persist).getName() + ", error: " + e.getMessage()); } } final Map<Integer, Integer> updatedElementIds = AbstractPersistFactory.resetUUIDSRecursively(clone, getPersistFactory(), false); if (clone instanceof ISupportChilds) { clone.acceptVisitor(new IPersistVisitor() { public Object visit(IPersist o) { if (o instanceof AbstractBase) { Map<String, Object> propertiesMap = ((AbstractBase)o).getPropertiesMap(); for (Map.Entry<String, Object> entry : propertiesMap.entrySet()) { Integer elementId = updatedElementIds.get(entry.getValue()); if (elementId != null) { Element element = StaticContentSpecLoader.getContentSpec().getPropertyForObjectTypeByName(o.getTypeID(), entry.getKey()); if (element.getTypeID() == IRepository.ELEMENTS) ((AbstractBase)o).setProperty(entry.getKey(), elementId); } } } return null; } }); } flush(persist); return clone; } public void deletePersistCopy(AbstractBase persist, boolean revertToOriginal) { if (mainSolution == null && loginFlattenedSolution != null) { loginFlattenedSolution.deletePersistCopy(persist, revertToOriginal); return; } if (copySolution != null) { copySolution.removeChild(persist); } flush(persist); if (persist instanceof Form) { refreshSuperForms(persist); } // if it has a clone persist property then it was cloned (and there is an original) // or revert was called on an original object then just ignore it. AbstractBase realPersist = persist.getRuntimeProperty(CLONE_PROPERTY); boolean exists = realPersist != null || getAllObjectsAsList().indexOf(persist) != -1; if (!exists && revertToOriginal) { throw new RuntimeException("Can't revert " + persist + " to original, because there is no original"); //$NON-NLS-1$//$NON-NLS-2$ } else if (exists && !revertToOriginal) { if (realPersist != null) { removedPersist.add(realPersist); flush(realPersist); } else { removedPersist.add(persist); } flush(persist); } } public void addToRemovedPersists(AbstractBase persist) { AbstractBase realPersist = persist.getRuntimeProperty(CLONE_PROPERTY); if (realPersist != null) { removedPersist.add(realPersist); } else { removedPersist.add(persist); } } public boolean hasCopy(IPersist persist) { if (mainSolution == null && loginFlattenedSolution != null) { return loginFlattenedSolution.hasCopy(persist); } return copySolution != null && copySolution.getChild(persist.getUUID()) != null; } public void updatePersistInSolutionCopy(final IPersist persist) { if (mainSolution == null && loginFlattenedSolution != null) { loginFlattenedSolution.updatePersistInSolutionCopy(persist); return; } if (copySolution == null) return; IPersist copyPersist = (IPersist)copySolution.acceptVisitor(new IPersistVisitor() { public Object visit(IPersist o) { if (o.getUUID().equals(persist.getUUID())) { return o; } return IPersistVisitor.CONTINUE_TRAVERSAL; } }); if (copyPersist != null) { ((AbstractBase)copyPersist).copyPropertiesMap(((AbstractBase)persist).getPropertiesMap(), false); ISupportChilds parent = copyPersist.getParent(); flush(persist); if (parent instanceof Form) { ((Form)parent).setLastModified(System.currentTimeMillis()); } else if (copyPersist instanceof Form) { ((Form)copyPersist).setLastModified(System.currentTimeMillis()); } } else if (persist.getParent() != null && !(persist.getParent() instanceof Solution)) { ISupportChilds copyParent = (ISupportChilds)copySolution.acceptVisitor(new IPersistVisitor() { public Object visit(IPersist o) { if (o.getUUID().equals(persist.getParent().getUUID())) { return o; } return IPersistVisitor.CONTINUE_TRAVERSAL; } }); if (copyParent != null) { if (persist instanceof ICloneable) { copyParent.addChild(((ICloneable)persist).clonePersist()); } else { copyParent.addChild(persist); } flush(persist); if (copyParent instanceof Form) { ((Form)copyParent).setLastModified(System.currentTimeMillis()); } } } } public Solution getSolutionCopy() { return getSolutionCopy(true); } public Solution getSolutionCopy(boolean create) { if (mainSolution == null) { if (loginFlattenedSolution != null) { return loginFlattenedSolution.getSolutionCopy(create); } return null; } if (copySolution != null || !create) return copySolution; try { SimplePersistFactory factory = getPersistFactory(); copySolution = SimplePersistFactory.createDummyCopy(mainSolution); copySolution.setChangeHandler(new ChangeHandler(factory)); copySolution.getChangeHandler().addIPersistListener(this); } catch (Exception e) { Debug.error(e); } return copySolution; } public TableNode getSolutionCopyTableNode(String dataSource) throws RepositoryException { return getSolutionCopy().getOrCreateTableNode(dataSource); } protected int getMaxID() { final int[] maxId = new int[1]; IPersistVisitor visitor = new IPersistVisitor() { public Object visit(IPersist o) { if (maxId[0] < o.getID()) { maxId[0] = o.getID(); } return IPersistVisitor.CONTINUE_TRAVERSAL; } }; mainSolution.acceptVisitor(visitor); if (modules != null) { for (Solution module : modules) { module.acceptVisitor(visitor); } } int max = maxId[0]; if (loginFlattenedSolution != null) { int loginMax = loginFlattenedSolution.getMaxID(); if (loginMax > max) { max = loginMax; } } return max; } private SimplePersistFactory getPersistFactory() { if (persistFactory == null) { persistFactory = new SimplePersistFactory(getMaxID()); } return persistFactory; } public Style createStyleCopy(Style style) { if (mainSolution == null && loginFlattenedSolution == null) return null; if (user_created_styles == null) user_created_styles = new HashMap<String, Style>(); Style copy = user_created_styles.get(style.getName()); if (copy == null) { copy = new Style(style.getRepository(), style.getRootObjectMetaData()); copy.setContent(style.getContent()); user_created_styles.put(copy.getName(), copy); } return copy; } public Style createStyle(String name, String content) { if (mainSolution == null) { if (loginFlattenedSolution == null) { return null; } return loginFlattenedSolution.createStyle(name, content); } if (getAllStyles().containsKey(name) && !deletedStyles.contains(name)) throw new RuntimeException("Style with name '" + name + "' already exists"); //$NON-NLS-1$ //$NON-NLS-2$ if (user_created_styles == null) user_created_styles = new HashMap<String, Style>(); if (user_created_styles.containsKey(name)) throw new RuntimeException("Style with name '" + name + "' already exists"); //$NON-NLS-1$ //$NON-NLS-2$ RootObjectMetaData rootObjectMetaData = new RootObjectMetaData(-1, UUID.randomUUID(), name, IRepository.STYLES, 1, 1); rootObjectMetaData.setName(name); Style style = new Style(mainSolution.getRepository(), rootObjectMetaData); style.setContent(content); user_created_styles.put(name, style); deletedStyles.remove(name); return style; } /** * @param style * @return */ public boolean isUserStyle(Style style) { if (mainSolution == null && loginFlattenedSolution != null) { return loginFlattenedSolution.isUserStyle(style); } if (style == null || user_created_styles == null) return false; return user_created_styles.containsKey(style.getName()); } public long getLastModifiedTime() { if (copySolution != null) return copySolution.getLastModifiedTime(); return getSolution().getLastModifiedTime(); } /** * @return */ public Solution getSolution() { if (mainSolution == null && loginFlattenedSolution != null) { return loginFlattenedSolution.getSolution(); } return mainSolution; } public boolean isMainSolutionLoaded() { return mainSolution != null; } private boolean isLoadingSolution = false; public void setSolution(SolutionMetaData sol, boolean loadLoginSolution, boolean loadMainSolution, IActiveSolutionHandler activeSolutionHandler) throws RepositoryException, RemoteException { if (sol == null) throw new IllegalArgumentException("use close method!"); //$NON-NLS-1$ isLoadingSolution = true; try { copySolution = null; persistFactory = null; mainSolution = null; user_created_styles = null; all_styles = null; mainSolutionMetaData = sol; if (loadLoginSolution) { // get login solution Solution[] loginSolutionAndModules = activeSolutionHandler.loadLoginSolutionAndModules(mainSolutionMetaData); if (loginSolutionAndModules != null && loginSolutionAndModules.length > 0) { if (loginFlattenedSolution == null) { loginFlattenedSolution = new FlattenedSolution(); } loginFlattenedSolution.setSolutionAndModules(loginSolutionAndModules[0].getName(), loginSolutionAndModules); loginFlattenedSolution.setParentSolution(this); } } // get regular solution if available if (loadMainSolution && activeSolutionHandler.haveRepositoryAccess()) // local or already access to repository { // Note: referencedModules includes main solution List<RootObjectReference> referencedModules = activeSolutionHandler.getRepository().getActiveSolutionModuleMetaDatas( mainSolutionMetaData.getRootObjectId()); if (referencedModules != null && referencedModules.size() != 0) { List<RootObjectMetaData> solutionAndModuleMetaDatas = new ArrayList<RootObjectMetaData>(); for (int idx = 0; idx < referencedModules.size(); idx++) { RootObjectReference moduleReference = referencedModules.get(idx); RootObjectMetaData moduleMetaData = moduleReference.getMetaData(); if (moduleMetaData == null) { throw new RepositoryException(Messages.getString("servoy.client.module.notfound", new Object[] { moduleReference.toString() })); //$NON-NLS-1$ } solutionAndModuleMetaDatas.add(moduleMetaData); } Solution[] mods = activeSolutionHandler.loadActiveSolutions(solutionAndModuleMetaDatas.toArray(new RootObjectMetaData[solutionAndModuleMetaDatas.size()])); setSolutionAndModules(mainSolutionMetaData.getName(), mods); } } } finally { isLoadingSolution = false; } } public boolean isLoadingSolution() { return isLoadingSolution; } protected void setSolutionAndModules(String mainSolutionName, Solution[] mods) throws RemoteException { Map<String, Solution> modulesMap = new HashMap<String, Solution>(); //name -> solution // Note: mods includes main solution for (Solution module : mods) { if (module != null) { if (mainSolutionName.equals(module.getName())) { mainSolution = module; } else if (!modulesMap.containsKey(module.getName())) { modulesMap.put(module.getName(), module); } } } if (mainSolution != null) { if (mainSolution.getChangeHandler() != null) { mainSolution.getChangeHandler().addIPersistListener(this); } for (Solution module : modulesMap.values()) { combineServerProxies(mainSolution.getServerProxies(), module.getServerProxies()); } getAllStyles(); } modules = getDependencyGraphOrderedModules(modulesMap.values(), mainSolution); for (Solution s : modules) { HashMap<String, Style> modStyles = s.getSerializableRuntimeProperty(Solution.PRE_LOADED_STYLES); if (modStyles != null) { synchronized (modStyles) { Map<String, Style> allStyles = getAllStyles(); synchronized (allStyles) // the two syncs should not cause deadlock because the module's lock is always acquired first (so another thread cannot come and do it backwards) { allStyles.putAll(modStyles); } } } if (s.getChangeHandler() != null) { s.getChangeHandler().addIPersistListener(this); } } // refresh all the extends forms, TODO this is kind of bad, because form instances are shared over clients. Iterator<Form> it = getForms(false); while (it.hasNext()) { Form childForm = it.next(); if (childForm.getExtendsID() > 0) { childForm.setExtendsForm(getForm(childForm.getExtendsID())); } } ; } /** * */ private Map<String, Style> getAllStyles() { if (all_styles == null) { all_styles = mainSolution.getSerializableRuntimeProperty(Solution.PRE_LOADED_STYLES); if (all_styles == null) { // this should normally not happen, because when solutions are loaded this property is set (by the repository) all_styles = new HashMap<String, Style>(); mainSolution.setSerializableRuntimeProperty(Solution.PRE_LOADED_STYLES, all_styles); } } return all_styles; } private static Solution[] getDependencyGraphOrderedModules(Collection<Solution> modules, Solution solution) { List<Solution> orderedSolutions = buildOrderedList(modules, new ArrayList<Solution>(), solution); if (orderedSolutions.size() > 1) { Collections.reverse(orderedSolutions); } return orderedSolutions.toArray(new Solution[orderedSolutions.size()]); } private static List<Solution> buildOrderedList(Collection<Solution> modules, List<Solution> orderedSolutions, Solution solution) { if (solution != null && solution.getModulesNames() != null) { for (String moduleName : Utils.getTokenElements(solution.getModulesNames(), ",", true)) //$NON-NLS-1$ { Solution module = null; for (Solution sol : modules) { if (sol.getName().equals(moduleName)) { module = sol; break; } } if (module != null && !orderedSolutions.contains(module)) { orderedSolutions.add(module); buildOrderedList(modules, orderedSolutions, module); } } } return orderedSolutions; } public void clearLoginSolution(IActiveSolutionHandler handler) { if (loginFlattenedSolution != null) { loginFlattenedSolution.close(handler); loginFlattenedSolution = null; flushAllCachedData(); } } public SolutionMetaData getMainSolutionMetaData() { return mainSolutionMetaData; } private void combineServerProxies(Map<String, IServer> orgs, Map<String, IServer> additional) throws RemoteException { synchronized (additional) { synchronized (orgs) // the two syncs should not cause deadlock because the module's lock is always acquired first (so another thread cannot come and do it backwards) { Iterator<IServer> it = additional.values().iterator(); while (it.hasNext()) { IServer addObject = it.next(); IServer orgObject = orgs.get(addObject.getName()); if (addObject instanceof ServerProxy) { ServerProxy add = (ServerProxy)addObject; if (orgObject == null) { orgs.put(add.getName(), add); } else if (orgObject instanceof ServerProxy) { ServerProxy org = (ServerProxy)orgObject; org.combineTables(add); } } } } } } /* * Get a flattened form from this flattened solution. * * <p>When the form does not have a parent, the form itself is returned */ public Form getFlattenedForm(IPersist persist) { return getFlattenedForm(persist, true); } public Form getFlattenedForm(IPersist persist, boolean useCached) { if (mainSolution == null && loginFlattenedSolution != null) { return loginFlattenedSolution.getFlattenedForm(persist, useCached); } if (persist == null || getSolution() == null) { return null; } Form form = null; if (persist instanceof FlattenedForm) { form = ((FlattenedForm)persist).getForm(); } else { form = (Form)persist.getAncestor(IRepository.FORMS); } if (form == null || form.getExtendsID() <= 0) { // no form or nothing to flatten return form; } if (flattenedFormCache == null) { // no caching return createFlattenedForm(form); } // cache flattened form FlattenedForm[] flattedFormRef; synchronized (flattenedFormCache) { flattedFormRef = flattenedFormCache.get(form); if (flattedFormRef == null) { flattenedFormCache.put(form, flattedFormRef = new FlattenedForm[] { null }); } } synchronized (flattedFormRef) { if (flattedFormRef[0] == null || !useCached) { flattedFormRef[0] = createFlattenedForm(form); } return flattedFormRef[0]; } } /* * Create a flattened form for the form that is child of the current solution. */ private FlattenedForm createFlattenedForm(Form form) { Form myForm; Solution solcopy = getSolutionCopy(false); if (form.getParent() == getSolution() || form.getParent() == solcopy) { myForm = form; } else { myForm = null; if (solcopy != null) { myForm = (Form)solcopy.getChild(form.getUUID()); } if (myForm == null) { myForm = (Form)getSolution().getChild(form.getUUID()); } if (myForm == null && modules != null) { for (Solution mod : modules) { myForm = (Form)mod.getChild(form.getUUID()); if (myForm != null) break; } } if (myForm == null) { throw new RuntimeException("Cannot find form '" + form + "' in solution '" + getSolution() + "'"); } } return new FlattenedForm(this, myForm); } public void flushFlattenedFormCache() { if (flattenedFormCache != null) { synchronized (flattenedFormCache) { flattenedFormCache.clear(); } } } /** * Check whether a form can be instantiated. * * @param form * @return */ public boolean formCanBeInstantiated(Form form) { if (form != null) { if (form.isResponsiveLayout()) return true; // forms without parts are abstract List<Form> formHierarchy = new ArrayList<Form>(); formHierarchy.add(form); Form f = form; while (true) { Iterator<IPersist> allObjects = f.getAllObjects(); while (allObjects.hasNext()) { if (allObjects.next() instanceof Part) { return true; } } if (form instanceof FlattenedForm || f.getExtendsID() <= 0) { // no (more) hierarchy to investigate break; } f = getForm(f.getExtendsID()); if (f == null || formHierarchy.contains(f) /* prevent cycles */) { break; } formHierarchy.add(f); } } return false; } int allObjectsSize = 256; List<IPersist> allObjectscache = null; public List<IPersist> getAllObjectsAsList() { if (mainSolution == null) { if (loginFlattenedSolution != null) { return loginFlattenedSolution.getAllObjectsAsList(); } return Collections.emptyList(); } if (allObjectscache == null) { List<IPersist> retval = new ArrayList<IPersist>(allObjectsSize); if (copySolution != null) { fillList(retval, copySolution); } getAllModuleObjects(retval); fillList(retval, mainSolution); if (loginFlattenedSolution != null) { List<IPersist> elements = loginFlattenedSolution.getAllObjectsAsList(); for (IPersist persist : elements) { if (!retval.contains(persist)) retval.add(persist); } } allObjectsSize = retval.size(); retval.removeAll(removedPersist); allObjectscache = retval; } return allObjectscache; } private void getAllModuleObjects(List<IPersist> copyInto) { if (modules != null) { for (Solution s : modules) { if (s != mainSolution) { fillList(copyInto, s); } } } return; } /** * @param copyInto * @param s */ private void fillList(List<IPersist> copyInto, Solution s) { List<IPersist> allObjectsAsList = s.getAllObjectsAsList(); if (copySolution == null || copySolution == s) { copyInto.addAll(allObjectsAsList); } else { Object[] copy = allObjectsAsList.toArray(); for (Object o : copy) { IPersist persist = (IPersist)o; if (copySolution.getChild(persist.getUUID()) == null) { copyInto.add(persist); } } } } public Collection<String> getScopeNames() { return getScopesPerSolution().keySet(); } public Collection<Pair<String, IRootObject>> getScopes() { return getScopesPerSolution().values(); } private Map<String, Pair<String, IRootObject>> getScopesPerSolution() { if (mainSolution == null && loginFlattenedSolution != null) { return loginFlattenedSolution.getScopesPerSolution(); } Map<String, Pair<String, IRootObject>> scopes = new HashMap<String, Pair<String, IRootObject>>(); if (mainSolution != null) { for (String scopeName : mainSolution.getScopeNames()) { scopes.put(scopeName, new Pair<String, IRootObject>(scopeName, mainSolution)); } if (modules != null) { for (Solution s : modules) { if (s != mainSolution) { for (String scopeName : s.getScopeNames()) { if (!scopes.containsKey(scopeName)) { // TODO: check if same scope exists with different modules scopes.put(scopeName, new Pair<String, IRootObject>(scopeName, s)); } } } } } } addGlobalsScope(scopes); return scopes; } protected void addGlobalsScope(Map<String, Pair<String, IRootObject>> scopes) { if (!scopes.containsKey(ScriptVariable.GLOBAL_SCOPE)) { // make sure there is always a globals scope scopes.put(ScriptVariable.GLOBAL_SCOPE, new Pair<String, IRootObject>(ScriptVariable.GLOBAL_SCOPE, mainSolution)); } } public List<Pair<String, IRootObject>> getAllScopes() { List<Pair<String, IRootObject>> scopes = new ArrayList<Pair<String, IRootObject>>(); if (mainSolution != null) { for (String scopeName : mainSolution.getScopeNames()) { scopes.add(new Pair<String, IRootObject>(scopeName, mainSolution)); } if (modules != null) { for (Solution s : modules) { if (s != mainSolution) { for (String scopeName : s.getScopeNames()) { scopes.add(new Pair<String, IRootObject>(scopeName, s)); } } } } } return scopes; } public void addSecurityAccess(Map<Object, Integer> sp) { addSecurityAccess(sp, true); } public void overrideSecurityAccess(Map<Object, Integer> sp) { addSecurityAccess(sp, false); } public void addSecurityAccess(Map<Object, Integer> sp, boolean combineIfExisting) { if (securityAccess == null) { securityAccess = new ConcurrentHashMap<Object, Integer>(sp); } else if (sp != null) { Iterator<Map.Entry<Object, Integer>> iterator = sp.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Object, Integer> entry = iterator.next(); Object elementUID = entry.getKey(); int newValue = entry.getValue().intValue(); Integer currentValue = securityAccess.get(elementUID); if (currentValue != null && combineIfExisting) { newValue |= currentValue.intValue(); } securityAccess.put(elementUID, new Integer(newValue)); } } } public void clearSecurityAccess() { if (securityAccess != null) { securityAccess.clear(); securityAccess = null; } } public int getSecurityAccess(Object element_id) { if (securityAccess == null) return -1; Integer i = securityAccess.get(element_id); if (i == null) { return -1; } else { return i.intValue(); } } //commit all user data public void close(IActiveSolutionHandler handler) { flushAllCachedData(); if (loginFlattenedSolution != null) { loginFlattenedSolution.close(handler); } loginFlattenedSolution = null; if (mainSolution != null) { if (mainSolution.getChangeHandler() != null) { mainSolution.getChangeHandler().removeIPersistListener(this); } if (handler != null) handler.saveActiveSolution(mainSolution);//try disksave in client mainSolution = null; } if (modules != null) { for (Solution element : modules) { if (handler != null) handler.saveActiveSolution(element);//try disksave in client if (element.getChangeHandler() != null) { element.getChangeHandler().removeIPersistListener(this); } } modules = null; } mainSolutionMetaData = null; synchronized (liveForms) { changedForms.clear(); liveForms.clear(); } designFormName = null; copySolution = null; persistFactory = null; removedPersist.clear(); allObjectsSize = 256; parentFlattenedSolution = null; } private static final IDataProvider NULL = new IDataProvider() { public boolean isEditable() { return false; } public int getLength() { return 0; } public int getFlags() { return 0; } public int getDataProviderType() { return 0; } public String getDataProviderID() { return null; } public ColumnWrapper getColumnWrapper() { return null; } }; public IDataProvider getGlobalDataProvider(String id) throws RepositoryException { return getGlobalDataProvider(id, false); } /** * @param quiet don't throw exceptions if tables/servers are not found; just do best effort search */ public IDataProvider getGlobalDataProvider(String id, boolean quiet) throws RepositoryException { if (id == null) return null; IDataProvider retval = globalProviders.get(id); if (retval == null) { retval = getGlobalDataProviderEx(id, quiet); if (retval != null) { globalProviders.put(id, retval); } else { globalProviders.put(id, NULL); } } return retval == NULL ? null : retval; } private IDataProvider getGlobalDataProviderEx(String id, boolean quiet) throws RepositoryException { if (id == null) return null; Pair<String, String> scope = ScopesUtils.getVariableScope(id); if (scope.getLeft() != null /* global scope */) { //search all objects,will return globals ScriptVariable global = AbstractBase.selectByName(getScriptVariables(scope.getLeft(), false), scope.getRight()); if (global != null) { return global; } // try @enum global variables return getEnumDataProvider(id); } int indx = id.lastIndexOf('.'); // in case of multi-level relations we have more that 1 dot if (indx > 0) { String rel_name = id.substring(0, indx); String col = id.substring(indx + 1); Relation[] relations = getRelationSequence(rel_name); if (relations == null) { return null; } Relation r = relations[relations.length - 1]; if (quiet) { boolean missingSrv = true; String ds = r.getForeignDataSource(); if (ds != null) { String[] st = DataSourceUtilsBase.getDBServernameTablename(ds); if (st != null && st.length == 2) { try { missingSrv = (r.getRootObject().getServer(st[0]) == null); } catch (RemoteException e) { // we are in developer here - shouldn't happen } } } if (missingSrv) return null; } Column[] cols = r.getForeignColumns(); if (cols == null || cols.length == 0) return null; IDataProvider c = getDataProviderForTable(r.getForeignTable(), col); if (r != null && c instanceof IColumn) { return new ColumnWrapper((IColumn)c, relations); } return c; } return null; } /** * @return * @throws RepositoryException */ protected IDataProvider getEnumDataProvider(String id) throws RepositoryException { // Note: this method is overridden in developer to add the correct type to EnumDataProviders String[] enumParts = id.split("\\."); //$NON-NLS-1$ if (enumParts.length > 3) { IDataProvider globalDataProvider = getGlobalDataProvider(enumParts[0] + '.' + enumParts[1] + '.' + enumParts[2]); if (globalDataProvider instanceof ScriptVariable && ((ScriptVariable)globalDataProvider).isEnum()) { return new EnumDataProvider(id, 0); // untyped } } return null; } /** * Returns script calculations and aggregates and columns */ public IDataProvider getDataProviderForTable(Table table, String dataProviderID) throws RepositoryException { if (table == null || dataProviderID == null) return null; // check for calculations first because of stored calculations Map<String, IDataProvider> dps = getAllDataProvidersForTable(table); IDataProvider dataProvider = null; if (dps != null) { dataProvider = dps.get(dataProviderID); } if (dataProvider != null) return dataProvider; Column column = table.getColumn(Ident.generateNormalizedName(dataProviderID)); if (column != null) { return column; } return null; } public synchronized Map<String, IDataProvider> getAllDataProvidersForTable(Table table) throws RepositoryException { if (table == null) return null; if (allProvidersForTable == null) allProvidersForTable = new ConcurrentHashMap<Table, Map<String, IDataProvider>>(64, 0.9f, 16); Map<String, IDataProvider> dataProvidersMap = allProvidersForTable.get(table); if (dataProvidersMap == null) { dataProvidersMap = new HashMap<String, IDataProvider>(16, 0.9f); //1) first the columns Iterator<Column> columns = table.getColumns().iterator(); while (columns.hasNext()) { Column col = columns.next(); ColumnInfo ci = col.getColumnInfo(); if (ci != null && ci.isExcluded()) { continue; } dataProvidersMap.put(col.getDataProviderID(), col); } //2) last the scriptcalculations and aggregates so the overlap the columns in case of stored calcs Iterator<TableNode> tableNodes = getTableNodes(table); while (tableNodes.hasNext()) { TableNode tableNode = tableNodes.next(); if (tableNode != null) { Iterator<IPersist> it2 = tableNode.getAllObjects(); while (it2.hasNext()) { IPersist persist = it2.next(); if (persist instanceof IDataProvider) { dataProvidersMap.put(((IDataProvider)persist).getDataProviderID(), (IDataProvider)persist); } } } } allProvidersForTable.put(table, dataProvidersMap); } return dataProvidersMap; } public synchronized void flushDataProvidersForPersist(IPersist persist) { if (persist == null || allProvidersForTable == null) return; TableNode tableNode = (TableNode)persist.getAncestor(IRepository.TABLENODES); if (tableNode != null && tableNode.getDataSource() != null) // it might reach this code from the persist created event - where data source is not yet set { try { Table table = tableNode.getTable(); if (table != null) { allProvidersForTable.remove(table); } } catch (RepositoryException e) { Debug.log(e); } } } public synchronized void flushDataProvidersForTable(Table table) { if (table != null && allProvidersForTable != null) { allProvidersForTable.remove(table); flushGlobalProviders(); } } public Collection<Style> flushUserStyles() { if (user_created_styles != null) { Collection<Style> col = user_created_styles.values(); user_created_styles = null; return col; } return Collections.emptyList(); } public synchronized void flushAllCachedData() { globalProviders.clear(); allProvidersForTable = null; relationCacheByName = null; scopeCacheByName = null; formCacheByName = null; formCacheById = null; valuelistCacheByName = null; dataProviderLookups = null; all_styles = null; beanDesignInstances = null; allObjectscache = null; flushFlattenedFormCache(); } private Map<IPersist, IDataProviderLookup> dataProviderLookups; public synchronized void flushDataProviderLookups(final IPersist p) { if (dataProviderLookups != null) { if (p instanceof Form) { // flush all forms to be sure it affects parent/child forms as well Iterator<IPersist> it = dataProviderLookups.keySet().iterator(); while (it.hasNext()) { if (it.next() instanceof Form) it.remove(); } } else { dataProviderLookups.remove(p); } } } public synchronized IDataProviderLookup getDataproviderLookup(IFoundSetManagerInternal foundSetManager, final IPersist p) { if (dataProviderLookups == null) dataProviderLookups = new HashMap<IPersist, IDataProviderLookup>(); IDataProviderLookup retval = dataProviderLookups.get(p); if (retval == null) { if (p instanceof Form) { ITable t = null; try { if (foundSetManager == null) { t = ((Form)p).getTable(); } else { t = foundSetManager.getTable(((Form)p).getDataSource()); } } catch (RepositoryException e) { Debug.error(e); } retval = new FormAndTableDataProviderLookup(this, (Form)p, t); } else if (p instanceof Portal) { Table t = null; try { Relation[] relations = getRelationSequence(((Portal)p).getRelationName()); if (relations == null) { return null; } t = relations[relations.length - 1].getForeignTable(); } catch (RepositoryException e) { Debug.error(e); } retval = new FormAndTableDataProviderLookup(this, (Form)p.getParent(), t); } else //solution { retval = new IDataProviderLookup() { public IDataProvider getDataProvider(String id) throws RepositoryException { return getGlobalDataProvider(id); } public Table getTable() throws RepositoryException { return null; } }; } dataProviderLookups.put(p, retval); } return retval; } public Solution[] getModules() { return modules; } public String[] getSolutionNames() { List<String> names = new ArrayList<String>(); if (mainSolution != null) { names.add(mainSolution.getName()); } if (modules != null) { for (Solution module : modules) { names.add(module.getName()); } } return names.toArray(new String[0]); } public boolean hasModule(String moduleName) { if (modules != null) { for (Solution module : modules) { if (module.getName().equals(moduleName)) { return true; } } } return false; } public synchronized Relation getRelation(String name) { if (name.indexOf('.') >= 0) { // if we get here the relation name should be analyzed using getRelationSequence(name). Debug.log("Unexpected relation name lookup", new Exception(name)); //$NON-NLS-1$ return null; } Map<String, Relation> tmp = relationCacheByName; if (tmp == null) { try { tmp = new HashMap<String, Relation>(32, 0.9f); Iterator<Relation> it = getRelations(false); while (it.hasNext()) { Relation r = it.next(); if (r != null) { tmp.put(r.getName(), r); } } relationCacheByName = tmp; } catch (RepositoryException ex) { Debug.error(ex); } } return tmp.get(name); } /** * Get relations from string rel1.rel2. ... .reln * * @param name * @return */ public synchronized Relation[] getRelationSequence(String name) { if (name == null) { return null; } String[] parts = name.split("\\."); //$NON-NLS-1$ Relation[] seq = new Relation[parts.length]; Relation prev = null; for (int i = 0; i < parts.length; i++) { Relation relation = getRelation(parts[i]); if (relation == null || (prev != null && (prev.getForeignDataSource() == null || !prev.getForeignDataSource().equals(relation.getPrimaryDataSource())))) { return null; } seq[i] = relation; prev = relation; } return seq; } private void flushRelations() { relationCacheByName = null; } private void flushValuelists() { valuelistCacheByName = null; } private void flushForms() { formCacheByName = null; formCacheById = null; } private void flushScopes() { scopeCacheByName = null; } private synchronized void flushScriptVariables() { scopeCacheByName = null; flushGlobalProviders(); } private synchronized void flushGlobalProviders() { globalProviders.clear(); } /** * Search for a persist by UUID in the main solution and all modules. * * @param uuid * @return */ public IPersist searchPersist(UUID uuid) { if (mainSolution != null) { if (copySolution != null) { IPersist persist = AbstractRepository.searchPersist(copySolution, uuid); if (persist != null) return persist; } IPersist persist = AbstractRepository.searchPersist(mainSolution, uuid); if (persist != null) { return persist; } if (modules != null) { for (Solution module : modules) { persist = AbstractRepository.searchPersist(module, uuid); if (persist != null) { return persist; } } } } if (loginFlattenedSolution != null) { return loginFlattenedSolution.searchPersist(uuid); } return null; } /** * Search for a persist in the parent tree in the main solution and all modules. * * @param persist * @return */ public IPersist searchPersist(IPersist persist) { if (mainSolution != null) { IPersist found = AbstractRepository.searchPersist(mainSolution, persist); if (found != null) { return found; } if (modules != null) { for (Solution module : modules) { found = AbstractRepository.searchPersist(module, persist); if (found != null) { return found; } } } } if (loginFlattenedSolution != null) { return loginFlattenedSolution.searchPersist(persist); } return null; } public void iPersistChanged(IPersist persist) { flush(persist); if (persist instanceof Form) { Form form = (Form)persist; if (form.getExtendsID() > 0) { if (form.getExtendsForm() == null || form.getExtendsForm().getID() != form.getExtendsID()) { form.setExtendsForm(getForm(form.getExtendsID())); } } else { form.setExtendsForm(null); } } } public void iPersistCreated(IPersist persist) { flush(persist); if (persist instanceof Form) { Form form = (Form)persist; if (form.getExtendsID() > 0) { form.setExtendsForm(getForm(form.getExtendsID())); } } } public void iPersistRemoved(IPersist persist) { flush(persist); } /** * @param persist */ public void setParentSolution(FlattenedSolution parent) { this.parentFlattenedSolution = parent; } public void flush(IPersist persist) { flush(persist, true); } public void flush(IPersist persist, boolean flushParent) { if (persist instanceof Relation) flushRelations(); if (persist instanceof ValueList) flushValuelists(); if (persist instanceof Form) flushForms(); if (persist instanceof ScriptMethod) flushScopes(); if (persist instanceof ScriptVariable) flushScriptVariables(); if (persist instanceof ScriptCalculation || persist instanceof AggregateVariable) flushGlobalProviders(); flushDataProvidersForPersist(persist); flushDataProviderLookups(persist); allObjectscache = null; if (loginFlattenedSolution != null) { loginFlattenedSolution.flush(persist, false); } if (flushParent && parentFlattenedSolution != null) { parentFlattenedSolution.flush(persist); } if (copySolution != null) copySolution.updateLastModifiedTime(); } public void removeStyle(String name) { if (user_created_styles != null && user_created_styles.containsKey(name)) { Style style = user_created_styles.remove(name); if (style != null) ComponentFactory.flushStyle(null, style); } else if (!deletedStyles.contains(name)) { deletedStyles.add(name); } } public synchronized Style getStyle(String name) { if (user_created_styles != null) { Style style = user_created_styles.get(name); if (style != null) return style; } if (deletedStyles.contains(name)) { return null; } if (loginFlattenedSolution != null) { return loginFlattenedSolution.getStyle(name); } Map<String, Style> allStyles = getAllStyles(); Style style = allStyles.get(name); if (style == null) { try { style = (Style)mainSolution.getRootObject().getRepository().getActiveRootObject(name, IRepository.STYLES); if (style != null) { synchronized (allStyles) { allStyles.put(name, style); } } } catch (Exception e) { Debug.error("error loading style '" + name + "'", e); //$NON-NLS-1$ //$NON-NLS-2$ } } return style; } public Style getStyleForForm(Form f, Map<String, String> overridenStyles) { if (loginFlattenedSolution != null) { return loginFlattenedSolution.getStyleForForm(f, overridenStyles); } synchronized (this) { String style_name = f.getStyleName(); if (deletedStyles.contains(style_name)) { return null; } Map<String, Style> allStyles = getAllStyles(); if (style_name != null) { String overridden = null; if (overridenStyles != null && (overridden = overridenStyles.get(style_name)) != null) { style_name = overridden; } if (user_created_styles != null) { Style style = user_created_styles.get(style_name); if (style != null) return style; } Style style = allStyles.get(style_name); if (style != null) { return style; } if (overridden != null) { try { style = (Style)f.getRootObject().getRepository().getActiveRootObject(overridden, IRepository.STYLES); if (style != null) { synchronized (allStyles) { allStyles.put(style.getName(), style); } return style; } } catch (Exception e) { Debug.error("Couldn't get the new style " + overridden + " for the overridden one:" + f.getStyleName()); //$NON-NLS-1$ //$NON-NLS-2$ Debug.error(e); } } } Style s = loadStyleForForm(this, f); if (s != null) { synchronized (allStyles) { allStyles.put(s.getName(), s); } } return s; } } /** * Load style for form, look for extend forms in flattenedSolution when possible * @param flattenedSolution may be null, fallback to f.getSolution() plus repo load * @param f */ public static Style loadStyleForForm(FlattenedSolution flattenedSolution, Form form) { try { String style_name = form.getStyleName(); if (!(form instanceof FlattenedForm)) { Form extendedForm; int extended_form_id = form.getExtendsID(); List<RootObjectReference> modulesMetaData = null; // We keep track of visited forms, to avoid infinite loop. Set<Integer> visited = new HashSet<Integer>(); while (style_name == null && extended_form_id > 0) { visited.add(new Integer(extended_form_id)); // if this is hit here with a normal (not flattened form) that has an extend. // and that form is a solution modal form. the f.getSolution() doesn't have to find it. // because f.getSolution() is the copy solution thats inside the FlattenedSolution. // that one doesn't have any other forms. But normally all forms should be flattened. extendedForm = flattenedSolution == null ? form.getSolution().getForm(extended_form_id) : flattenedSolution.getForm(extended_form_id); if (flattenedSolution == null && extendedForm == null) // check for module form { if (modulesMetaData == null) { modulesMetaData = form.getSolution().getRepository().getActiveSolutionModuleMetaDatas(form.getSolution().getID()); } for (RootObjectReference moduleMetaData : modulesMetaData) { Solution module = (Solution)form.getSolution().getRepository().getActiveRootObject(moduleMetaData.getMetaData().getRootObjectId()); extendedForm = module.getForm(extended_form_id); if (extendedForm != null) { break; } } } if (extendedForm == null) { break;//in case referring to no longer existing form } style_name = extendedForm.getStyleName(); extended_form_id = extendedForm.getExtendsID(); if (visited.contains(new Integer(extended_form_id))) { break;//in case of cycle in form inheritance hierarchy } } } if (style_name == null) return null; Style s = (Style)form.getSolution().getRepository().getActiveRootObject(style_name, IRepository.STYLES);//preload the style at the server return s; } catch (Exception e) { Debug.error(e); } return null; } public synchronized void flushBeanDesignInstance(Bean b) { if (beanDesignInstances != null) { beanDesignInstances.remove(b); } } /** * set bean design instance if not set yet. * @param b * @param arg * @return new value or old value if instance was already set */ public synchronized Object setBeanDesignInstance(Bean b, Object arg) { if (beanDesignInstances == null) { beanDesignInstances = new ConcurrentHashMap<Bean, Object>(); } else { Object object = beanDesignInstances.get(b); if (object != null) { return object; } } beanDesignInstances.put(b, arg); return arg; } public synchronized Object getBeanDesignInstance(Bean b) { return beanDesignInstances == null ? null : beanDesignInstances.get(b); } public Iterator<ScriptMethod> getScriptMethods(String scopeName, boolean sort) { return Solution.getScriptMethods(getAllObjectsAsList(), scopeName, sort); } public Iterator<ScriptMethod> getScriptMethods(boolean sort) { return Solution.getScriptMethods(getAllObjectsAsList(), null, sort); } public ScriptMethod getScriptMethod(int methodId) { if (methodId <= 0) return null; return AbstractBase.selectById(getScriptMethods(null, false), methodId); } public ScriptMethod getScriptMethod(String methodNameOrUUID) { if (methodNameOrUUID == null) return null; if (methodNameOrUUID.indexOf('-') > 0) { UUID uuid = null; try { uuid = UUID.fromString(methodNameOrUUID); } catch (IllegalArgumentException e) { // not a uuid } if (uuid != null) { IPersist method = searchPersist(uuid); if (method instanceof ScriptMethod) { return (ScriptMethod)method; } } } return getScriptMethod(null, methodNameOrUUID); } /** * Get global script method. * Scope name may be in methodName (globals.x, scopes.globals.x or scopes.myscope.x) or may be supplied in arg scopeName. */ public ScriptMethod getScriptMethod(String scopeName, String methodName) { ISupportScope elem = getGlobalScopeElement(scopeName, methodName); if (elem instanceof ScriptMethod) { return (ScriptMethod)elem; } return null; } private ISupportScope getGlobalScopeElement(String sc, String elementName) { if (elementName == null) { return null; } String scopeName = sc; String baseName = elementName; if (sc == null) { Pair<String, String> scope = ScopesUtils.getVariableScope(elementName); scopeName = scope.getLeft(); baseName = scope.getRight(); if (scopeName == null) { scopeName = ScriptVariable.GLOBAL_SCOPE; } } else { baseName = ScopesUtils.getVariableScope(elementName).getRight(); } Map<String, ISupportScope> scopeMap = getGlobalScopeCache().get(scopeName); if (scopeMap == null) { return null; } return scopeMap.get(baseName); } private Map<String, Map<String, ISupportScope>> getGlobalScopeCache() { Map<String, Map<String, ISupportScope>> tmp = scopeCacheByName; if (tmp == null) { tmp = new HashMap<String, Map<String, ISupportScope>>(); for (IPersist persist : getAllObjectsAsList()) { if (persist instanceof ISupportScope) { String scopeName = ((ISupportScope)persist).getScopeName(); if (scopeName == null) { scopeName = ScriptVariable.GLOBAL_SCOPE; } Map<String, ISupportScope> scopeMap = tmp.get(scopeName); if (scopeMap == null) { tmp.put(scopeName, scopeMap = new HashMap<String, ISupportScope>()); } scopeMap.put(((ISupportScope)persist).getName(), (ISupportScope)persist); } } scopeCacheByName = tmp; } return tmp; } public Iterator<ScriptVariable> getScriptVariables(boolean sort) { return Solution.getScriptVariables(getAllObjectsAsList(), null, sort); } public Iterator<ScriptVariable> getScriptVariables(String scopeName, boolean sort) { return Solution.getScriptVariables(getAllObjectsAsList(), scopeName, sort); } /** * Get global script variable. * Scope name may be in variable (globals.x, scopes.globals.x or scopes.myscope.x) or may be supplied in arg scopeName. */ public ScriptVariable getScriptVariable(String scopeName, String variableName) { ISupportScope elem = getGlobalScopeElement(scopeName, variableName); if (elem instanceof ScriptVariable) { return (ScriptVariable)elem; } return null; } protected IRepository getRepository() { if (mainSolution != null) { return mainSolution.getRepository(); } if (loginFlattenedSolution != null) { return loginFlattenedSolution.getRepository(); } return null; } public Iterator<TableNode> getTableNodes(String dataSource) { return Solution.getTableNodes(getAllObjectsAsList(), dataSource); } public Iterator<TableNode> getTableNodes(ITable table) throws RepositoryException { return Solution.getTableNodes(getRepository(), getAllObjectsAsList(), table); } public Iterator<Relation> getRelations(boolean sort) throws RepositoryException { return Solution.getRelations(getAllObjectsAsList(), sort); } public Iterator<Relation> getRelations(ITable filterOnTable, boolean isPrimaryTable, boolean sort, boolean addGlobalsWhenPrimary, boolean onlyGlobalsWhenForeign, boolean onlyLiteralsWhenForeign) throws RepositoryException { return Solution.getRelations(getRepository(), getAllObjectsAsList(), filterOnTable, isPrimaryTable, sort, addGlobalsWhenPrimary, onlyGlobalsWhenForeign, onlyLiteralsWhenForeign); } public Iterator<Relation> getRelations(ITable filterOnTable, boolean isPrimaryTable, boolean sort) throws RepositoryException { return Solution.getRelations(getRepository(), getAllObjectsAsList(), filterOnTable, isPrimaryTable, sort); } public Iterator<ValueList> getValueLists(boolean sort) { return Solution.getValueLists(getAllObjectsAsList(), sort); } public ValueList getValueList(int id) { if (id <= 0) return null; return AbstractBase.selectById(getValueLists(false), id); } public ValueList getValueList(String name) { Map<String, ValueList> tmp = valuelistCacheByName; if (tmp == null) { tmp = new HashMap<String, ValueList>(32, 0.9f); Iterator<ValueList> valuelists = getValueLists(false); while (valuelists.hasNext()) { ValueList valuelist = valuelists.next(); tmp.put(valuelist.getName(), valuelist); } valuelistCacheByName = tmp; } return tmp.get(name); } public Iterator<Form> getForms(ITable basedOnTable, boolean sort) { return Solution.getForms(getAllObjectsAsList(), basedOnTable == null ? null : DataSourceUtils.createDBTableDataSource(basedOnTable.getServerName(), basedOnTable.getName()), sort); } public Iterator<Form> getForms(String datasource, boolean sort) { return Solution.getForms(getAllObjectsAsList(), datasource, sort); } public Iterator<Form> getForms(boolean sort) { return Solution.getForms(getAllObjectsAsList(), null, sort); } public Form getForm(int id) { if (id <= 0) return null; IntHashMap<Form> tmp = formCacheById; while (tmp == null) { // the form cache was null, try to create it fillFormCaches(); // can become null if a flush did happen in the mean time, then try again tmp = formCacheById; } return tmp.get(id); } public Form getForm(String name) { if (name == null) return null; Map<String, Form> tmp = formCacheByName; while (tmp == null) { // the form cache was null, try to create it fillFormCaches(); // can become null if a flush did happen in the mean time, then try again tmp = formCacheByName; } return tmp.get(name); } /** * @return */ private void fillFormCaches() { Map<String, Form> tmpByName = new HashMap<String, Form>(64, 0.9f); IntHashMap<Form> tmpById = new IntHashMap<Form>(64, 0.9f); Iterator<Form> forms = getForms(false); while (forms.hasNext()) { Form form = forms.next(); tmpByName.put(form.getName(), form); tmpById.put(form.getID(), form); } formCacheByName = tmpByName; formCacheById = tmpById; } /** * @param form * @return form hierarchy list [ form, form.super, form.super.super, ... ] */ public List<Form> getFormHierarchy(Form form) { List<Form> formHierarchy = new ArrayList<Form>(); formHierarchy.add(form); Form f = form; while (f != null && f.getExtendsID() > 0) { f = getForm(f.getExtendsID()); if (f == null || formHierarchy.contains(f) /* prevent cycles */) { break; } formHierarchy.add(f); } return formHierarchy; } public List<Form> getDirectlyInheritingForms(Form parentForm) { List<Form> inheritingForms = new ArrayList<Form>(); Iterator<Form> sameTableFormsIte = getForms(false); while (sameTableFormsIte.hasNext()) { Form sameTableForm = sameTableFormsIte.next(); if (sameTableForm.getExtendsID() == parentForm.getID()) { inheritingForms.add(sameTableForm); } } return inheritingForms; } public Iterator<AggregateVariable> getAggregateVariables(ITable basedOnTable, boolean sort) throws RepositoryException { return Solution.getAggregateVariables(getTableNodes(basedOnTable), sort); } public ScriptCalculation getScriptCalculation(String name, ITable basedOnTable) { if (basedOnTable == null) { return null; } return getScriptCalculation(name, basedOnTable.getDataSource()); } public ScriptCalculation getScriptCalculation(String name, String basedOnDataSource) { return AbstractBase.selectByName(getScriptCalculations(basedOnDataSource, false), name); } public Iterator<ScriptCalculation> getScriptCalculations(ITable basedOnTable, boolean sort) { if (basedOnTable == null) { return Collections.<ScriptCalculation> emptyList().iterator(); } return getScriptCalculations(basedOnTable.getDataSource(), sort); } public Iterator<ScriptCalculation> getScriptCalculations(String basedOnDataSource, boolean sort) { List<ScriptCalculation> scriptCalculations = Solution.getScriptCalculations(getTableNodes(basedOnDataSource), sort); if (copySolution != null) { Iterator<TableNode> tableNodes = copySolution.getTableNodes(basedOnDataSource); // if there is a copy solution with a tablenode on that table if (tableNodes.hasNext()) { // remove all script calcs with the same name as the calc which isnt the copy calc itself. Iterator<ScriptCalculation> copyCalcs = tableNodes.next().getScriptCalculations(); while (copyCalcs.hasNext()) { ScriptCalculation copyCalc = copyCalcs.next(); for (int i = scriptCalculations.size(); i-- != 0;) { ScriptCalculation scriptCalculation = scriptCalculations.get(i); if (copyCalc.getName().equals(scriptCalculation.getName()) && copyCalc != scriptCalculation) { scriptCalculations.remove(i); break; } } } } } //remove the deleted calculation from the deletedPersists scriptCalculations.removeAll(removedPersist); return scriptCalculations.iterator(); } public ScriptMethod getFoundsetMethod(String name, String basedOnDataSource) { return AbstractBase.selectByName(getFoundsetMethods(basedOnDataSource, false), name); } public Iterator<ScriptMethod> getFoundsetMethods(String basedOnDatasource, boolean sort) { if (basedOnDatasource == null) { return Collections.<ScriptMethod> emptyList().iterator(); } List<ScriptMethod> foundsetMethods = Solution.getFoundsetMethods(getTableNodes(basedOnDatasource), sort); if (copySolution != null) { Iterator<TableNode> tableNodes = copySolution.getTableNodes(basedOnDatasource); // if there is a copy solution with a tablenode on that table if (tableNodes.hasNext()) { // remove all script methods with the same name as the method which isnt the copy method itself. Iterator<ScriptMethod> copyMethods = tableNodes.next().getFoundsetMethods(false); while (copyMethods.hasNext()) { ScriptMethod copyMethod = copyMethods.next(); for (int i = foundsetMethods.size(); i-- != 0;) { ScriptMethod method = foundsetMethods.get(i); if (copyMethod.getName().equals(method.getName()) && copyMethod != method) { foundsetMethods.remove(i); break; } } } } } //remove the deleted methods from the deletedPersists for (ScriptMethod method : foundsetMethods) { if (removedPersist.contains(method)) { foundsetMethods.remove(method); } } return foundsetMethods.iterator(); } public List<ScriptMethod> getFoundsetMethods(ITable basedOnTable, boolean sort) throws RepositoryException { List<ScriptMethod> foundsetMethods = Solution.getFoundsetMethods(getTableNodes(basedOnTable), sort); if (copySolution != null) { Iterator<TableNode> tableNodes = copySolution.getTableNodes(basedOnTable); // if there is a copy solution with a tablenode on that table if (tableNodes.hasNext()) { // remove all script methods with the same name as the method which isnt the copy method itself. Iterator<ScriptMethod> copyMethods = tableNodes.next().getFoundsetMethods(false); while (copyMethods.hasNext()) { ScriptMethod copyMethod = copyMethods.next(); for (int i = foundsetMethods.size(); i-- != 0;) { ScriptMethod scriptCalculation = foundsetMethods.get(i); if (copyMethod.getName().equals(scriptCalculation.getName()) && copyMethod != scriptCalculation) { foundsetMethods.remove(i); break; } } } } } //remove the deleted calculation from the deletedPersists for (ScriptMethod method : foundsetMethods) { if (removedPersist.contains(method)) { foundsetMethods.remove(method); } } return foundsetMethods; } public Iterator<Media> getMedias(boolean sort) { return Solution.getMedias(getAllObjectsAsList(), sort); } public Media getMedia(int id) { if (id <= 0) return null; return AbstractBase.selectById(getMedias(false), id); } public Media getMedia(String name) { return AbstractBase.selectByName(getMedias(false), name); } /** * Get the internal relation that can be used to sort on this value list using the display values. * @param valueList * @param callingTable * @param dataProviderID * @param foundSetManager * @return * @throws RepositoryException */ public Relation getValuelistSortRelation(ValueList valueList, Table callingTable, String dataProviderID, IFoundSetManagerInternal foundSetManager) throws RepositoryException { if (callingTable == null || valueList == null) { return null; } String destDataSource; Relation[] relationSequence; String relationPrefix; switch (valueList.getDatabaseValuesType()) { case IValueListConstants.TABLE_VALUES : // create an internal relation relationSequence = null; relationPrefix = ""; //$NON-NLS-1$ destDataSource = valueList.getDataSource(); break; case IValueListConstants.RELATED_VALUES : // replace the last relation in the sequence with an internal relation relationSequence = getRelationSequence(valueList.getRelationName()); if (relationSequence == null) { return null; } if (relationSequence.length > 1) { for (Relation r : relationSequence) { if (r.getJoinType() != ISQLJoin.INNER_JOIN) { // disabled related vl sorting for muti-level related VLs, // outer join on the intermediate tables causes extra results that influence the sorting result return null; } } } StringBuilder sb = new StringBuilder(); for (Relation r : relationSequence) { sb.append('-').append(r.getName()); } relationPrefix = sb.toString(); destDataSource = relationSequence[relationSequence.length - 1].getForeignDataSource(); break; default : return null; } if (destDataSource == null || !DataSourceUtils.isSameServer(callingTable.getDataSource(), destDataSource)) { // do not create a cross-server relation return null; } Table destTable = (Table)foundSetManager.getTable(destDataSource); if (destTable == null) { return null; } String relationName = Relation.INTERNAL_PREFIX + "VL-" + callingTable.getDataSource() + '-' + dataProviderID + relationPrefix + '-' + valueList.getName() + '-'; //$NON-NLS-1$ synchronized (this) { Column callingColumn = callingTable.getColumn(dataProviderID); if (callingColumn == null) { return null; } Relation relation = getRelation(relationName); if (relation == null) { // create in internal relation String dp; int returnValues = valueList.getReturnDataProviders(); if ((returnValues & 1) != 0) { dp = valueList.getDataProviderID1(); } else if ((returnValues & 2) != 0) { dp = valueList.getDataProviderID2(); } else if ((returnValues & 4) != 0) { dp = valueList.getDataProviderID3(); } else { return null; } Column destColumn = destTable.getColumn(dp); if (destColumn == null) { return null; } // create internal value list relation QueryTable callingQTable = new QueryTable(callingTable.getSQLName(), callingTable.getDataSource(), callingTable.getCatalog(), callingTable.getSchema()); QueryTable destQTable = new QueryTable(destTable.getSQLName(), destTable.getDataSource(), destTable.getCatalog(), destTable.getSchema()); List<ISQLTableJoin> joins = new ArrayList<ISQLTableJoin>(); ISQLTableJoin lastJoin = null; if (relationSequence == null) { // table values joins.add(lastJoin = new QueryJoin(relationName, callingQTable, destQTable, new AndCondition(), ISQLJoin.LEFT_OUTER_JOIN)); if (valueList.getUseTableFilter()) //apply name as filter on column valuelist_name { lastJoin.getCondition().addCondition( new CompareCondition(IBaseSQLCondition.EQUALS_OPERATOR, new QueryColumn(destQTable, DBValueList.NAME_COLUMN), valueList.getName())); } } else { // related values QueryTable primaryQTable = callingQTable; for (int i = 0; i < relationSequence.length; i++) { Relation r = relationSequence[i]; QueryTable foreignQTable; if (i == relationSequence.length - 1) { // last one foreignQTable = destQTable; } else { Table relForeignTable = r.getForeignTable(); if (relForeignTable == null) { return null; } foreignQTable = new QueryTable(relForeignTable.getSQLName(), relForeignTable.getDataSource(), relForeignTable.getCatalog(), relForeignTable.getSchema()); } lastJoin = SQLGenerator.createJoin(this, r, primaryQTable, foreignQTable, new IGlobalValueEntry() { public Object setDataProviderValue(String dpid, Object value) { return null; } public Object getDataProviderValue(String dpid) { // A value will be added when the relation is used, see SQLGenerator.createJoin return new Placeholder(new ObjectPlaceholderKey<int[]>(null, dpid)); } public boolean containsDataProvider(String dpid) { return false; } }); joins.add(lastJoin); primaryQTable = foreignQTable; } } // add condition for return dp id lastJoin.getCondition().addCondition( new CompareCondition(IBaseSQLCondition.EQUALS_OPERATOR, new QueryColumn(destQTable, destColumn.getID(), destColumn.getSQLName(), destColumn.getType(), destColumn.getLength(), destColumn.getScale(), destColumn.getFlags()), new QueryColumn(callingQTable, callingColumn.getID(), callingColumn.getSQLName(), callingColumn.getType(), callingColumn.getLength(), callingColumn.getScale(), callingColumn.getFlags()))); relation = getSolutionCopy().createNewRelation(new ScriptNameValidator(this), relationName, callingTable.getDataSource(), destDataSource, ISQLJoin.LEFT_OUTER_JOIN); ISQLTableJoin join; if (joins.size() == 1) { join = lastJoin; } else { // combine joins join = new QueryCompositeJoin(relationName, joins); } relation.setRuntimeProperty(Relation.RELATION_JOIN, join); } return relation; } } /** * @see java.lang.Object#toString() */ @SuppressWarnings("nls") @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("FlattenedSolution of solution: "); if (mainSolution == null) { sb.append("<NONE>"); } else { sb.append(mainSolution.getName()); sb.append(" having modules: ").append(Arrays.toString(modules)); } if (loginFlattenedSolution != null) { sb.append(" Login solution: ").append(loginFlattenedSolution.toString()); } return sb.toString(); } /** * @param form * @param namedInstance */ public void deregisterLiveForm(Form form, String namedInstance) { synchronized (liveForms) { if (form instanceof FlattenedForm) { for (Form f : ((FlattenedForm)form).getAllForms()) { deregisterLiveForm(f, namedInstance); } } else { Set<String> instances = liveForms.get(form.getName()); if (instances != null) { instances.remove(namedInstance); if (instances.size() == 0) { liveForms.remove(form.getName()); } } } // Test forms if they now are ok. (form they build on when they where changed is now destroyed) ChangedFormData changedFormData = changedForms.get(form.getName()); if (changedFormData != null) { changedFormData.instances.remove(namedInstance); if (changedFormData.instances.size() == 0) { changedForms.remove(form.getName()); } } } } /** * @param form */ public void registerLiveForm(Form form, String namedInstance) { synchronized (liveForms) { if (form instanceof FlattenedForm) { for (Form f : ((FlattenedForm)form).getAllForms()) { registerLiveForm(f, namedInstance); } } else { Set<String> instances = liveForms.get(form.getName()); if (instances == null) { instances = new HashSet<String>(); liveForms.put(form.getName(), instances); } instances.add(namedInstance); } } } /** * @param form * @param application */ public void registerChangedForm(Form form) { synchronized (liveForms) { if (liveForms.containsKey(form.getName()) && !isInDesign(form)) { ChangedFormData changedFormData = changedForms.get(form.getName()); if (changedFormData == null) { // if liveForms does contain the form then remember it. changedFormData = new ChangedFormData(liveForms.get(form.getName())); changedForms.put(form.getName(), changedFormData); } else { // only interested in the last line that altered the form. changedFormData.infos.clear(); // the the forms that are currently live. changedFormData.instances.addAll(liveForms.get(form.getName())); } if (debugListener != null) { debugListener.addDebugInfo(changedFormData.infos); } } } } public void registerDebugListener(IFlattenedSolutionDebugListener listener) { this.debugListener = listener; } public IFlattenedSolutionDebugListener getDebugListener() { return debugListener; } /** * */ @SuppressWarnings("nls") public void checkStateForms(IServiceProvider provider) { synchronized (liveForms) { if (changedForms.size() > 0) { StringBuilder sb = new StringBuilder(); sb.append("The form(s) that are stale (can also be a parent form if form inheritance is used) are:\n\t"); //$NON-NLS-1$ for (String fName : changedForms.keySet()) { Set<String> set = changedForms.get(fName).instances; sb.append("Form name:'"); sb.append(fName); sb.append("' with instances: ["); for (String instanceName : set) { sb.append(instanceName); sb.append(','); } sb.setLength(sb.length() - 1); sb.append("]"); Set<Object> infos = changedForms.get(fName).infos; if (infos != null && infos.size() == 2) { sb.append("\n\t\tat "); sb.append(infos.toArray()[0] instanceof Integer ? infos.toArray()[1] : infos.toArray()[0]); sb.append(":"); //$NON-NLS-1$ sb.append(infos.toArray()[0] instanceof Integer ? infos.toArray()[0] : infos.toArray()[1]); sb.append("\n"); //$NON-NLS-1$ } sb.append("\n\t"); //$NON-NLS-1$ } sb.setLength(sb.length() - 1); changedForms.clear(); provider.reportError("Stale form(s) detected, form(s) were altered by the solution model without destroying them first", sb.toString()); //$NON-NLS-1$ } } } private String designFormName = null; /** * Called by the Template generator to generate a record view html when the form is in design and in TABLEVIEW * @param f * @return */ public boolean isInDesign(Form f) { if (f == null) return designFormName != null; return f.getName().equals(designFormName); } /** * @param form */ public void setInDesign(Form form) { if (form == null) { designFormName = null; } else if (designFormName != null && !form.getName().equals(designFormName)) { throw new IllegalStateException("cant have 2 forms in design mode"); //$NON-NLS-1$ } else { designFormName = form.getName(); } } private void refreshSuperForms(IPersist persist) { if (persist != null) { Iterator<Form> it = getForms(false); while (it.hasNext()) { Form childForm = it.next(); if (childForm.getID() == persist.getID() || childForm.getExtendsID() == persist.getID()) { // this is an adjustment of a sub form make sure we create a copy first. if (childForm.getID() != persist.getID() && getSolutionCopy().getChild(childForm.getUUID()) == null) { childForm = createPersistCopy(childForm); registerChangedForm(childForm); } childForm.setExtendsForm(getForm(childForm.getExtendsID())); } } } } private static class ChangedFormData { private final Set<Object> infos = new HashSet<Object>(); private final Set<String> instances; private ChangedFormData(Set<String> instances) { // make copy, so that this set is stable from this moment this.instances = new HashSet<String>(instances); } } public Form revertForm(String name) { Form form = getForm(name); if (form == null) { // if this form is not found, then it could be in the removedPersist // then revert the remove/delete for (IPersist persist : removedPersist) { if (persist instanceof Form && name.equals(((Form)persist).getName())) { removedPersist.remove(persist); flush(persist); form = getForm(name); break; } } } if (form != null) { deletePersistCopy(form, true); form = getForm(name); registerChangedForm(form); } return form; } public String getName() { Solution solution = getSolution(); return solution != null ? solution.getName() : null; } }