gt:       lStorageLocationName = lStorageLocationDefinition.value();
context: package net.foxopen.fox.module.entrytheme;
  
  import net.foxopen.fox.XFUtil;
  import net.foxopen.fox.command.XDoCommandList;
  import net.foxopen.fox.dom.DOM;
  import net.foxopen.fox.dom.DOMList;
  import net.foxopen.fox.dom.SchemaUtils;
  import net.foxopen.fox.entrypoint.auth.AuthType;
  import net.foxopen.fox.entrypoint.auth.AuthenticationType;
  import net.foxopen.fox.ex.ExCardinality;
  import net.foxopen.fox.ex.ExDoSyntax;
  import net.foxopen.fox.ex.ExInternal;
  import net.foxopen.fox.ex.ExModule;
  import net.foxopen.fox.ex.ExUserRequest;
  import net.foxopen.fox.ex.ExValidation;
  import net.foxopen.fox.module.Mod;
  import net.foxopen.fox.module.SyncMode;
  import net.foxopen.fox.module.Validatable;
  import net.foxopen.fox.thread.storage.DataDOMStorageLocation;
  import net.foxopen.fox.track.Track;
  import net.foxopen.fox.track.Trackable;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  
  public final class EntryTheme
  implements Trackable, Validatable {
  
    //TODO NP/PN - convert to enums
    public static final String gTypeServiceRPC = "service-rpc";
    public static final String gTypeServiceDocument = "service-document";
    public static final String gTypeInternal = "internal";
    public static final String gTypeExternal = "external";
  
    private static final Set<String> VALID_THEME_TYPES = new HashSet<>();
    static {
      VALID_THEME_TYPES.add(gTypeServiceRPC);
      VALID_THEME_TYPES.add(gTypeServiceDocument);
      VALID_THEME_TYPES.add(gTypeInternal);
      VALID_THEME_TYPES.add(gTypeExternal);
    }
  
    private static final String SYNC_MODE_ATTR_NAME = "synchronisation-mode";
    private static final String VALIDATION_ENABLED_ATTR_NAME = "validation-enabled";
  
    private final Mod mModule;
    private final String mThemeName;
    private final String mAttachXPath;
    private final String mStateName;
    private final XDoCommandList mXDo;
    private final XDoCommandList mBeforeEntryXDo;
    private final List<DataDOMStorageLocation> mStorageLocationList;
    private final DataDOMStorageLocation mDefaultStorageLocation;
    private final Map<String, SyncMode> mStorageLocationNamesToSyncModes;
    private final Map<String, Boolean> mStorageLocationValidationFlags;
    private final String mType;
    private final boolean mPublished;
    private final EntryThemeSecurity mEntryThemeSecurity;
    private final Map<String, ThemeParam> mHeaderParamsMap = new HashMap<>();
    private final Map<String, ThemeParam> mParamsMap = new HashMap<>();
    private final List<ThemeParam> mParamsArray = new ArrayList<>();
    private final Map<String, ThemeParam> mReturnMap = new HashMap<>();
    private final List<ThemeParam> mReturnArray = new ArrayList<>();
    private final Map<String, ThemeParam> mMandParamsMap = new HashMap<>();
    private final Map<String, ThemeParam> mMandReturnMap = new HashMap<>();
    private final boolean mIsWebServiceOperation;
    private final AuthenticationType mAuthenticationType;
    private final boolean mIsThreadPersistent;
    private final boolean mDefaultEntryTheme;
    private final boolean mAllowedPasswordExpiredAccess;
  
    public EntryTheme(Mod pModule, DOM pEntryThemeDOM)
    throws ExModule, ExDoSyntax, ExInternal {
      // Set basic theme attributes
      mModule = pModule;
  
      // Set theme name
      mThemeName = pEntryThemeDOM.getAttr("name");
      if(mThemeName.length()==0){
        throw new ExModule("Bad entry-theme name (must not be empty string)", pEntryThemeDOM);
      }
  
      try {
        // Default the module type to internal
        mType = XFUtil.nvl(pEntryThemeDOM.getAttr("type"), gTypeInternal);
        if(!VALID_THEME_TYPES.contains(mType)) {
          throw new ExModule("Bad entry-theme type " + mType, pEntryThemeDOM);
        }
  
        // Set boolean so consumers can tell that we're acting as a web service operation
        mIsWebServiceOperation = (mType.equals(gTypeServiceRPC) || mType.equals(gTypeServiceDocument));
  
        // Default to unpublished
        mPublished = Boolean.valueOf(XFUtil.nvl(pEntryThemeDOM.getAttr("published"), "false"));
  
        // Default to not persistent
        mIsThreadPersistent = Boolean.valueOf(XFUtil.nvl(pEntryThemeDOM.getAttr("persistent-thread"), "false"));
  
        // Default to not allowed
        mAllowedPasswordExpiredAccess = Boolean.valueOf(XFUtil.nvl(pEntryThemeDOM.getAttr("allow-expired-access"), "false"));
  
        // Default to HTTP authentication for web services or portal otherwise
        String lAuthenticationType = pEntryThemeDOM.getAttr("authentication-type");
        try {
          if (!XFUtil.isNull(lAuthenticationType)) {
            mAuthenticationType = AuthenticationType.fromString(lAuthenticationType);
          }
          else {
            mAuthenticationType = (mIsWebServiceOperation ? AuthenticationType.HTTP_AUTH_BASIC : AuthenticationType.FOX_COMMAND);
          }
        }
        catch (ExInternal ex) {
          throw new ExModule("Bad entry-theme authentication type " + lAuthenticationType, pEntryThemeDOM, ex);
        }
  
        DOM lSecurityDOM = pEntryThemeDOM.get1EOrNull("fm:security");
        if(lSecurityDOM == null) {
          mEntryThemeSecurity = DefaultEntryThemeSecurity.instance();
        }
        else {
          mEntryThemeSecurity = DefinedEntryThemeSecurity.create(lSecurityDOM, pModule);
        }
  
        // Determine if this is the default entry theme
        mDefaultEntryTheme = Boolean.valueOf(XFUtil.nvl(pEntryThemeDOM.getAttr("default"), "false"));
  
        // Get attach point
        try {
          mAttachXPath = pEntryThemeDOM.get1S("fm:attach");
        }
        catch(ExCardinality x) {
          throw new ExModule("Bad entry-theme attach point", pEntryThemeDOM,x);
        }
  
        // Get state
        try {
          mStateName = pEntryThemeDOM.get1S("fm:state");
        }
        catch(ExCardinality x) {
          throw new ExModule("Bad entry-theme state", pEntryThemeDOM,x);
        }
  
        //Parse the storage location definition (may be in a list)
        StorageLocationParseResult lParseResult = parseStorageLocationDefinition(pEntryThemeDOM);
        mStorageLocationList = lParseResult.getStorageLocationList();
        mDefaultStorageLocation = lParseResult.getDefaultStorageLocation();
        mStorageLocationNamesToSyncModes = lParseResult.getStorageLocationSyncModes();
        mStorageLocationValidationFlags = lParseResult.getStorageLocationValidationFlags();
  
        // Look up "do" actions
        DOM lDoBlock;
        try {
          lDoBlock = pEntryThemeDOM.get1E("fm:do");
        }
        catch (ExCardinality x) {
          throw new ExModule("Bad entry-theme do", pEntryThemeDOM,x);
        }
        mXDo = new XDoCommandList(pModule, lDoBlock);
  
        //Parse before entry do block if specified
        DOM lBeforeEntryDoBlock = pEntryThemeDOM.get1EOrNull("fm:before-entry");
        if(lBeforeEntryDoBlock != null) {
          mBeforeEntryXDo = new XDoCommandList(pModule, lBeforeEntryDoBlock);
        }
        else {
          mBeforeEntryXDo = XDoCommandList.emptyCommandList();
        }
  
        //TODO subclass for web service entry themes
  
        // Retrieve the storage location root element name
        String lStoreLocationElemName = mDefaultStorageLocation.getNewDocRootElementName();
  
        // Parse module parameters
        DOMList lParamsList = pEntryThemeDOM.getUL("fm:param-list/fm:param");
        for (int i = 0; i < lParamsList.getLength(); i++) {
          ThemeParam lThemeParam = new ThemeParam(lParamsList.item(i), pModule, lStoreLocationElemName);
          if (!mParamsMap.containsKey(lThemeParam.getName())) {
            mParamsMap.put(lThemeParam.getName(), lThemeParam);
            if (lThemeParam.isMand()) {
              mMandParamsMap.put(lThemeParam.getName(), lThemeParam);
            }
            mParamsArray.add(lThemeParam);
          }
          else {
            throw new ExModule("Entry-theme param elements must be uniquely named or have XPaths that map to unique element names, duplicate found: '" + lThemeParam.getName() + "'.");
          }
        }
  
        DOMList lHeaderParamsList = pEntryThemeDOM.getUL("fm:header-param-list/fm:header-param");
        for (int i = 0; i < lHeaderParamsList.getLength(); i++) {
          ThemeParam lThemeParam = new ThemeParam(lHeaderParamsList.item(i), pModule, lStoreLocationElemName);
          if (!mHeaderParamsMap.containsKey(lThemeParam.getName())) {
            mHeaderParamsMap.put(lThemeParam.getName(), lThemeParam);
    //        if (lThemeParam.isMand()) {
    //          mHeaderParamsMap.put(lThemeParam.getName(), lThemeParam);
    //        }
          }
          else {
            throw new ExModule("Entry-theme header param elements must be uniquely named or have XPaths that map to unique element names, duplicate found: '" + lThemeParam.getName() + "'.");
          }
        }
  
        // Parse return values
        DOMList lReturnList = pEntryThemeDOM.getUL("fm:return-list/fm:return");
        for (int i = 0; i < lReturnList.getLength(); i++) {
          ThemeParam lThemeParam = new ThemeParam(lReturnList.item(i), pModule, lStoreLocationElemName);
          if (!mReturnMap.containsKey(lThemeParam.getName())) {
            mReturnMap.put(lThemeParam.getName(), lThemeParam);
            if (lThemeParam.isMand()) {
              mMandReturnMap.put(lThemeParam.getName(), lThemeParam);
            }
            mReturnArray.add(lThemeParam);
          }
          else {
            throw new ExModule("Entry-theme return elements must be uniquely named or have XPaths that map to unique element names, duplicate found: '" + lThemeParam.getName() + "'.");
          }
        }
      }
      catch (Throwable th) {
        throw new ExModule("Error in entry-theme definition " + mThemeName, th);
      }
    }
  
    private interface StorageLocationParseResult {
      List<DataDOMStorageLocation> getStorageLocationList();
      DataDOMStorageLocation getDefaultStorageLocation();
      Map<String, SyncMode> getStorageLocationSyncModes();
      Map<String, Boolean> getStorageLocationValidationFlags();
    }
  
    private StorageLocationParseResult parseStorageLocationDefinition(DOM pEntryThemeDOM)
    throws ExModule {
  
      //Search for a single SL definition or a list
      DOM lStorageLocationDefinition;
      String lStorageLocationName;
      try {
        lStorageLocationDefinition = pEntryThemeDOM.get1E("fm:storage-location");
  