+#----------------------------------------------------------------------------
+# Name: project.py
+# Purpose: project model for wx.lib.pydocview
+#
+# Author: Morgan Hua
+#
+# Created: 8/25/05
+# CVS-ID: $Id$
+# Copyright: (c) 2005 ActiveGrid, Inc.
+# License: wxWindows License
+#----------------------------------------------------------------------------
+
+import copy
+import os
+import os.path
+import activegrid.util.xmlutils as xmlutils
+from IDE import ACTIVEGRID_BASE_IDE
+if not ACTIVEGRID_BASE_IDE:
+ import activegrid.model.basedocmgr as basedocmgr
+ import AppInfo
+
+#----------------------------------------------------------------------------
+# Constants
+#----------------------------------------------------------------------------
+# Always add new versions, never edit the version number
+# This allows you to upgrade the file by checking the version number
+PROJECT_VERSION_050730 = '10'
+PROJECT_VERSION_050826 = '11'
+
+
+#----------------------------------------------------------------------------
+# XML Marshalling Methods
+#----------------------------------------------------------------------------
+
+def load(fileObject):
+ version = xmlutils.getAgVersion(fileObject.name)
+ # most current versions on top
+ if version == PROJECT_VERSION_050826:
+ fileObject.seek(0)
+ if ACTIVEGRID_BASE_IDE:
+ KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile}
+ else:
+ KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile, "ag:appInfo" : AppInfo.AppInfo}
+ project = xmlutils.load(fileObject.name, knownTypes=KNOWNTYPES, knownNamespaces=xmlutils.KNOWN_NAMESPACES)
+ elif version == PROJECT_VERSION_050730:
+ fileObject.seek(0)
+ project = xmlutils.load(fileObject.name, knownTypes={"project" : Project_10})
+ project = project.upgradeVersion()
+ else:
+ # assume it is old version without version number
+ fileObject.seek(0)
+ project = xmlutils.load(fileObject.name, knownTypes={"project" : Project_10})
+ if project:
+ project = project.upgradeVersion()
+ else:
+ print "Project, unknown version:", version
+ return None
+
+ if project:
+ project._projectDir = os.path.dirname(fileObject.name)
+ project.RelativeToAbsPath()
+
+ return project
+
+
+def save(fileObject, project, productionDeployment=False):
+ if not project._projectDir:
+ project._projectDir = os.path.dirname(fileObject.name)
+ project.AbsToRelativePath() # temporarily change it to relative paths for saving
+ if ACTIVEGRID_BASE_IDE:
+ KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile}
+ else:
+ KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile, "ag:appInfo" : AppInfo.AppInfo}
+
+ savedHomeDir = project.homeDir
+ if productionDeployment:
+ # for deployments, we don't want an abs path in homeDir since that
+ # would tie the app to the current filesystem. So unset it.
+ project.homeDir = None
+
+ xmlutils.save(fileObject.name, project, prettyPrint=True, knownTypes=KNOWNTYPES, knownNamespaces=xmlutils.KNOWN_NAMESPACES)
+
+ if productionDeployment:
+ project.homeDir = savedHomeDir
+
+ project.RelativeToAbsPath() # swap it back to absolute path
+
+
+#----------------------------------------------------------------------------
+# Classes
+#----------------------------------------------------------------------------
+
+class BaseProject(object):
+
+ __xmlname__ = "project"
+ __xmlexclude__ = ('fileName', '_projectDir', '_getDocCallback')
+ __xmlattributes__ = ("_homeDir", "version")
+ __xmlrename__ = { "_homeDir":"homeDir", "_appInfo":"appInfo" }
+ __xmlflattensequence__ = { "_files":("file",) }
+ __xmldefaultnamespace__ = xmlutils.AG_NS_URL
+ __xmlattrnamespaces__ = { "ag": ["version", "_homeDir"] }
+
+
+ def __init__(self):
+ self.__xmlnamespaces__ = { "ag" : xmlutils.AG_NS_URL }
+ self.version = PROJECT_VERSION_050826
+ self._files = []
+ self._projectDir = None # default for homeDir, set on load
+ self._homeDir = None # user set homeDir for use in calculating relative path
+ if not ACTIVEGRID_BASE_IDE:
+ self._appInfo = AppInfo.AppInfo()
+
+
+ def __copy__(self):
+ clone = Project()
+ clone._files = [copy.copy(file) for file in self._files]
+ clone._projectDir = self._projectDir
+ clone._homeDir = self._homeDir
+ if not ACTIVEGRID_BASE_IDE:
+ clone._appInfo = self._appInfo
+ return clone
+
+
+ def initialize(self):
+ """ Required method for xmlmarshaller """
+ pass
+
+
+ def GetAppInfo(self):
+ return self._appInfo
+
+
+ def AddFile(self, filePath=None, logicalFolder=None, type=None, name=None, file=None):
+ """ Usage: self.AddFile(filePath, logicalFolder, type, name) # used for initial generation of object
+ self.AddFile(file=xyzFile) # normally used for redo/undo
+ Add newly created file object using filePath and logicalFolder or given file object
+ """
+ if file:
+ self._files.append(file)
+ else:
+ self._files.append(ProjectFile(filePath, logicalFolder, type, name, getDocCallback=self._getDocCallback))
+
+
+ def RemoveFile(self, file):
+ self._files.remove(file)
+
+
+ def FindFile(self, filePath):
+ if filePath:
+ for file in self._files:
+ if file.filePath == filePath:
+ return file
+
+ return None
+
+
+ def _GetFilePaths(self):
+ return [file.filePath for file in self._files]
+
+
+ filePaths = property(_GetFilePaths)
+
+
+ def _GetLogicalFolders(self):
+ folders = []
+ for file in self._files:
+ if file.logicalFolder and file.logicalFolder not in folders:
+ folders.append(file.logicalFolder)
+ return folders
+
+
+ logicalFolders = property(_GetLogicalFolders)
+
+
+ def _GetPhysicalFolders(self):
+ physicalFolders = []
+ for file in self._files:
+ physicalFolder = file.physicalFolder
+ if physicalFolder and physicalFolder not in physicalFolders:
+ physicalFolders.append(physicalFolder)
+ return physicalFolders
+
+
+ physicalFolders = property(_GetPhysicalFolders)
+
+
+ def _GetHomeDir(self):
+ if self._homeDir:
+ return self._homeDir
+ else:
+ return self._projectDir
+
+
+ def _SetHomeDir(self, parentPath):
+ self._homeDir = parentPath
+
+
+ def _IsDefaultHomeDir(self):
+ return (self._homeDir == None)
+
+
+ isDefaultHomeDir = property(_IsDefaultHomeDir)
+
+
+ homeDir = property(_GetHomeDir, _SetHomeDir)
+
+
+ def GetRelativeFolders(self):
+ relativeFolders = []
+ for file in self._files:
+ relFolder = file.GetRelativeFolder(self.homeDir)
+ if relFolder and relFolder not in relativeFolders:
+ relativeFolders.append(relFolder)
+ return relativeFolders
+
+
+ def AbsToRelativePath(self):
+ for file in self._files:
+ file.AbsToRelativePath(self.homeDir)
+
+
+ def RelativeToAbsPath(self):
+ for file in self._files:
+ file.RelativeToAbsPath(self.homeDir)
+
+
+ #----------------------------------------------------------------------------
+ # BaseDocumentMgr methods
+ #----------------------------------------------------------------------------
+
+
+ def fullPath(self, fileName):
+ fileName = super(BaseProject, self).fullPath(fileName)
+
+ if os.path.isabs(fileName):
+ absPath = fileName
+ elif self.homeDir:
+ absPath = os.path.join(self.homeDir, fileName)
+ else:
+ absPath = os.path.abspath(fileName)
+ return os.path.normpath(absPath)
+
+
+ def documentRefFactory(self, name, fileType, filePath):
+ return ProjectFile(filePath=self.fullPath(filePath), type=fileType, name=name, getDocCallback=self._getDocCallback)
+
+
+ def findAllRefs(self):
+ return self._files
+
+
+ def GetXFormsDirectory(self):
+ forms = self.findRefsByFileType(basedocmgr.FILE_TYPE_XFORM)
+ filePaths = map(lambda form: form.filePath, forms)
+ xformdir = os.path.commonprefix(filePaths)
+ if not xformdir:
+ xformdir = self.homeDir
+ return xformdir
+
+
+ def setRefs(self, files):
+ self._files = files
+
+
+ def findRefsByFileType(self, fileType):
+ fileList = []
+ for file in self._files:
+ if fileType == file.type:
+ fileList.append(file)
+ return fileList
+
+
+ def GenerateServiceRefPath(self, wsdlFilePath):
+ # HACK: temporary solution to getting wsdlag path from wsdl path.
+ import wx
+ from WsdlAgEditor import WsdlAgDocument
+ ext = WsdlAgDocument.WSDL_AG_EXT
+ for template in wx.GetApp().GetDocumentManager().GetTemplates():
+ if template.GetDocumentType() == WsdlAgDocument:
+ ext = template.GetDefaultExtension()
+ break;
+ wsdlAgFilePath = os.path.splitext(wsdlFilePath)[0] + ext
+ return wsdlAgFilePath
+
+
+ def SetDocCallback(self, getDocCallback):
+ self._getDocCallback = getDocCallback
+ for file in self._files:
+ file._getDocCallback = getDocCallback
+
+
+if ACTIVEGRID_BASE_IDE:
+ class Project(BaseProject):
+ pass
+else:
+ class Project(BaseProject, basedocmgr.BaseDocumentMgr):
+ pass
+
+
+class ProjectFile(object):
+ __xmlname__ = "file"
+ __xmlexclude__ = ('_getDocCallback', '_docCallbackCacheReturnValue', '_docModelCallbackCacheReturnValue', '_doc',)
+ __xmlattributes__ = ["filePath", "logicalFolder", "type", "name"]
+ __xmldefaultnamespace__ = xmlutils.AG_NS_URL
+
+
+ def __init__(self, filePath=None, logicalFolder=None, type=None, name=None, getDocCallback=None):
+ self.filePath = filePath
+ self.logicalFolder = logicalFolder
+ self.type = type
+ self.name = name
+ self._getDocCallback = getDocCallback
+ self._docCallbackCacheReturnValue = None
+ self._docModelCallbackCacheReturnValue = None
+ self._doc = None
+
+
+ def _GetDocumentModel(self):
+ # possible bug is if document gets replaced outside of IDE, where we'll return previous doc.
+ # originally added a timestamp check but that increased the time again to 4x
+ if self._docModelCallbackCacheReturnValue: # accelerator for caching document, got 4x speed up.
+ return self._docModelCallbackCacheReturnValue
+
+ if self._getDocCallback:
+ self._docCallbackCacheReturnValue, self._docModelCallbackCacheReturnValue = self._getDocCallback(self.filePath)
+ return self._docModelCallbackCacheReturnValue
+
+ return None
+
+
+ document = property(_GetDocumentModel)
+
+
+ def _GetDocument(self):
+ # Hack, just return the IDE document wrapper that corresponds to the runtime document model
+ # callers should have called ".document" before calling ".ideDocument"
+ if self._docCallbackCacheReturnValue: # accelerator for caching document, got 4x speed up.
+ return self._docCallbackCacheReturnValue
+ return None
+
+
+ ideDocument = property(_GetDocument)
+
+
+ def _typeEnumeration(self):
+ return basedocmgr.FILE_TYPE_LIST
+
+
+ def _GetPhysicalFolder(self):
+ dir = None
+ if self.filePath:
+ dir = os.path.dirname(self.filePath)
+ if os.sep != '/':
+ dir = dir.replace(os.sep, '/') # require '/' as delimiter
+ return dir
+
+
+ physicalFolder = property(_GetPhysicalFolder)
+
+
+ def GetRelativeFolder(self, parentPath):
+ parentPathLen = len(parentPath)
+
+ dir = None
+ if self.filePath:
+ dir = os.path.dirname(self.filePath)
+ if dir.startswith(parentPath):
+ dir = "." + dir[parentPathLen:] # convert to relative path
+ if os.sep != '/':
+ dir = dir.replace(os.sep, '/') # always save out with '/' as path separator for cross-platform compatibility.
+ return dir
+
+
+ def AbsToRelativePath(self, parentPath):
+ """ Used to convert path to relative path for saving (disk format) """
+ parentPathLen = len(parentPath)
+
+ if self.filePath.startswith(parentPath):
+ self.filePath = "." + self.filePath[parentPathLen:] # convert to relative path
+ if os.sep != '/':
+ self.filePath = self.filePath.replace(os.sep, '/') # always save out with '/' as path separator for cross-platform compatibility.
+ else:
+ pass # not a decendant of project, use absolute path
+
+
+ def RelativeToAbsPath(self, parentPath):
+ """ Used to convert path to absolute path (for any necessary disk access) """
+ if self.filePath.startswith("."): # relative to project file
+ self.filePath = os.path.normpath(os.path.join(parentPath, self.filePath))
+
+
+ #----------------------------------------------------------------------------
+ # BaseDocumentMgr methods
+ #----------------------------------------------------------------------------
+
+ def _GetDoc(self):
+ # HACK: temporary solution.
+ import wx
+ import wx.lib.docview
+ if not self._doc:
+ docMgr = wx.GetApp().GetDocumentManager()
+
+ doc = docMgr.CreateDocument(self.filePath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
+ if (doc == None): # already open
+ docs = docMgr.GetDocuments()
+ for d in docs:
+ if d.GetFilename() == self.filePath:
+ doc = d
+ break
+ self._doc = doc
+ return self._doc
+
+
+ def _GetLocalServiceProcessName(self):
+ # HACK: temporary solution to getting process name from wsdlag file.
+ return self._GetDoc().GetModel().processName
+
+
+ processName = property(_GetLocalServiceProcessName)
+
+
+ def _GetStateful(self):
+ # HACK: temporary solution to getting stateful from wsdlag file.
+ return self._GetDoc().GetModel().stateful
+
+
+ def _SetStateful(self, stateful):
+ # HACK: temporary solution to setting stateful from wsdlag file.
+ self._GetDoc().GetModel().stateful = stateful
+
+
+ stateful = property(_GetStateful, _SetStateful)
+
+
+ def _GetLocalServiceCodeFile(self):
+ # HACK: temporary solution to getting class name from wsdlag file.
+ return self._GetDoc().GetModel().localServiceCodeFile
+
+
+ def _SetLocalServiceCodeFile(self, codefile):
+ # HACK: temporary solution to setting class name from wsdlag file.
+ self._GetDoc().GetModel().localServiceCodeFile = codefile
+
+
+ localServiceCodeFile = property(_GetLocalServiceCodeFile, _SetLocalServiceCodeFile)
+
+
+ def _GetLocalServiceClassName(self):
+ # HACK: temporary solution to getting class name from wsdlag file.
+ return self._GetDoc().GetModel().localServiceClassName
+
+
+ def _SetLocalServiceClassName(self, className):
+ # HACK: temporary solution to setting class name from wsdlag file.
+ self._GetDoc().GetModel().localServiceClassName = className
+
+
+ localServiceClassName = property(_GetLocalServiceClassName, _SetLocalServiceClassName)
+
+
+
+# only activate this code if we programatically need to access these values
+## def _GetRssServiceBaseURL(self):
+## return self._GetDoc().GetModel().rssServiceBaseURL
+##
+##
+## def _SetRssServiceBaseURL(self, baseURL):
+## self._GetDoc().GetModel().rssServiceBaseURL = baseURL
+##
+##
+## rssServiceBaseURL = property(_GetRssServiceBaseURL, _SetRssServiceBaseURL)
+##
+##
+## def _GetRssServiceRssVersion(self):
+## return self._GetDoc().GetModel().rssServiceRssVersion
+##
+##
+## def _SetRssServiceRssVersion(self, rssVersion):
+## self._GetDoc().GetModel().rssServiceRssVersion = rssVersion
+##
+##
+## rssServiceRssVersion = property(_GetRssServiceRssVersion, _SetRssServiceRssVersion)
+
+
+ def _GetServiceRefServiceType(self):
+ # HACK: temporary solution to getting service type from wsdlag file.
+ model = self._GetDoc().GetModel()
+ if hasattr(model, 'serviceType'):
+ return model.serviceType
+ else:
+ return None
+
+
+ def _SetServiceRefServiceType(self, serviceType):
+ # HACK: temporary solution to getting service type from wsdlag file.
+ self._GetDoc().GetModel().serviceType = serviceType
+
+
+ serviceType = property(_GetServiceRefServiceType, _SetServiceRefServiceType)
+
+
+ def getExternalPackage(self):
+ # HACK: temporary solution to getting custom code filename from wsdlag file.
+ import activegrid.server.deployment as deploymentlib
+
+ appInfo = self._GetDoc().GetAppInfo()
+
+ if appInfo.language == None:
+ language = deploymentlib.LANGUAGE_DEFAULT
+ else:
+ language = appInfo.language
+
+ if language == deploymentlib.LANGUAGE_PYTHON:
+ suffix = ".py"
+ elif language == deploymentlib.LANGUAGE_PHP:
+ suffix = ".php"
+ pyFilename = self.name + suffix
+ return self._GetDoc().GetAppDocMgr().fullPath(pyFilename)
+
+
+#----------------------------------------------------------------------------
+# Old Classes
+#----------------------------------------------------------------------------
+
+class Project_10:
+ """ Version 1.0, kept for upgrading to latest version. Over time, this should be deprecated. """
+ __xmlname__ = "project"
+ __xmlrename__ = { "_files":"files"}
+ __xmlexclude__ = ('fileName',)
+ __xmlattributes__ = ["version"]
+
+
+ def __init__(self):
+ self.version = PROJECT_VERSION_050730
+ self._files = []
+
+
+ def initialize(self):
+ """ Required method for xmlmarshaller """
+ pass
+
+
+ def upgradeVersion(self):
+ currModel = Project()
+ for file in self._files:
+ currModel._files.append(ProjectFile(file))
+ return currModel
+
+