/**
   * Compiles and caches a regexp pattern for the given string pattern.
   *
   * @param stringPattern a pattern string
   * @param caseSensitive case sensitive true/false
   * @return compiles and caches a regexp pattern for the given string pattern
   * @throws MalformedPatternException
   */
  private Pattern getTestPattern(String stringPattern, boolean caseSensitive)
      throws MalformedPatternException {
    Pattern pattern = compiledPatterns.get(stringPattern);
    if (pattern == null) {
      if (caseSensitive) {
        pattern = compiler.compile(stringPattern);
      } else {
        pattern = compiler.compile(stringPattern, Perl5Compiler.CASE_INSENSITIVE_MASK);
      }

      compiledPatterns.put(stringPattern, pattern);
      Debug.logVerbose(
          "Compiled and cached a pattern: '" + stringPattern + "' - " + Thread.currentThread(),
          module);
    }
    return pattern;
  }
Beispiel #2
0
  public static ModelReader getModelReader(String delegatorName) throws GenericEntityException {
    DelegatorInfo delegatorInfo = EntityConfigUtil.getDelegatorInfo(delegatorName);

    if (delegatorInfo == null) {
      throw new GenericEntityConfException(
          "Could not find a delegator with the name " + delegatorName);
    }

    String tempModelName = delegatorInfo.entityModelReader;
    ModelReader reader = readers.get(tempModelName);

    if (reader == null) {
      reader = new ModelReader(tempModelName);
      // preload caches...
      reader.getEntityCache();
      reader = readers.putIfAbsentAndGet(tempModelName, reader);
    }
    return reader;
  }
 private static FlexibleStringExpander getInstance(
     String expression, char[] chars, int offset, int length, boolean useCache) {
   if (length == 0) {
     return nullExpr;
   }
   if (!useCache) {
     return parse(chars, offset, length);
   }
   // Remove the next nine lines to cache all expressions
   if (!expression.contains(openBracket)) {
     if (chars.length == length) {
       return new ConstSimpleElem(chars);
     } else {
       return new ConstOffsetElem(chars, offset, length);
     }
   }
   Key key = chars.length == length ? new SimpleKey(chars) : new OffsetKey(chars, offset, length);
   FlexibleStringExpander fse = exprCache.get(key);
   if (fse == null) {
     exprCache.put(key, parse(chars, offset, length));
     fse = exprCache.get(key);
   }
   return fse;
 }
Beispiel #4
0
/** Generic Entity - Entity Definition Reader */
@SuppressWarnings("serial")
public class ModelReader implements Serializable {

  public static final String module = ModelReader.class.getName();
  private static final UtilCache<String, ModelReader> readers =
      UtilCache.createUtilCache("entity.ModelReader", 0, 0);

  protected Map<String, ModelEntity> entityCache = null;

  protected int numEntities = 0;
  protected int numViewEntities = 0;
  protected int numFields = 0;
  protected int numRelations = 0;
  protected int numAutoRelations = 0;
  protected int numUniqueKeys = 0;

  protected String modelName;

  /** collection of filenames for entity definitions */
  protected Collection<ResourceHandler> entityResourceHandlers;

  /**
   * contains a collection of entity names for each ResourceHandler, populated as they are loaded
   */
  protected Map<ResourceHandler, Collection<String>> resourceHandlerEntities;

  /** for each entity contains a map to the ResourceHandler that the entity came from */
  protected Map<String, ResourceHandler> entityResourceHandlerMap;

  public static ModelReader getModelReader(String delegatorName) throws GenericEntityException {
    DelegatorInfo delegatorInfo = EntityConfigUtil.getDelegatorInfo(delegatorName);

    if (delegatorInfo == null) {
      throw new GenericEntityConfException(
          "Could not find a delegator with the name " + delegatorName);
    }

    String tempModelName = delegatorInfo.entityModelReader;
    ModelReader reader = readers.get(tempModelName);

    if (reader == null) {
      reader = new ModelReader(tempModelName);
      // preload caches...
      reader.getEntityCache();
      reader = readers.putIfAbsentAndGet(tempModelName, reader);
    }
    return reader;
  }

  public ModelReader(String modelName) throws GenericEntityException {
    this.modelName = modelName;
    entityResourceHandlers = FastList.newInstance();
    resourceHandlerEntities = FastMap.newInstance();
    entityResourceHandlerMap = FastMap.newInstance();

    EntityModelReaderInfo entityModelReaderInfo =
        EntityConfigUtil.getEntityModelReaderInfo(modelName);

    if (entityModelReaderInfo == null) {
      throw new GenericEntityConfException(
          "Cound not find an entity-model-reader with the name " + modelName);
    }

    // get all of the main resource model stuff, ie specified in the entityengine.xml file
    for (Element resourceElement : entityModelReaderInfo.resourceElements) {
      ResourceHandler handler =
          new MainResourceHandler(EntityConfigUtil.ENTITY_ENGINE_XML_FILENAME, resourceElement);
      entityResourceHandlers.add(handler);
    }

    // get all of the component resource model stuff, ie specified in each ofbiz-component.xml file
    for (ComponentConfig.EntityResourceInfo componentResourceInfo :
        ComponentConfig.getAllEntityResourceInfos("model")) {
      if (modelName.equals(componentResourceInfo.readerName)) {
        entityResourceHandlers.add(componentResourceInfo.createResourceHandler());
      }
    }
  }

  private ModelEntity buildEntity(
      ResourceHandler entityResourceHandler, Element curEntityElement, int i, ModelInfo def)
      throws GenericEntityException {
    boolean isEntity = "entity".equals(curEntityElement.getNodeName());
    String entityName = UtilXml.checkEmpty(curEntityElement.getAttribute("entity-name")).intern();

    // add entityName to appropriate resourceHandlerEntities collection
    Collection<String> resourceHandlerEntityNames =
        resourceHandlerEntities.get(entityResourceHandler);

    if (resourceHandlerEntityNames == null) {
      resourceHandlerEntityNames = FastList.newInstance();
      resourceHandlerEntities.put(entityResourceHandler, resourceHandlerEntityNames);
    }
    resourceHandlerEntityNames.add(entityName);

    // check to see if entity with same name has already been read
    if (entityCache.containsKey(entityName)) {
      Debug.logWarning(
          "WARNING: Entity "
              + entityName
              + " is defined more than once, most recent will over-write "
              + "previous definition(s)",
          module);
      Debug.logWarning(
          "WARNING: Entity "
              + entityName
              + " was found in "
              + entityResourceHandler
              + ", but was already defined in "
              + entityResourceHandlerMap.get(entityName).toString(),
          module);
    }

    // add entityName, entityFileName pair to entityResourceHandlerMap map
    entityResourceHandlerMap.put(entityName, entityResourceHandler);

    // utilTimer.timerString("  After entityEntityName -- " + i + " --");
    // ModelEntity entity = createModelEntity(curEntity, utilTimer);

    ModelEntity modelEntity = null;
    if (isEntity) {
      modelEntity = createModelEntity(curEntityElement, null, def);
    } else {
      modelEntity = createModelViewEntity(curEntityElement, null, def);
    }

    String resourceLocation = entityResourceHandler.getLocation();
    try {
      resourceLocation = entityResourceHandler.getURL().toExternalForm();
    } catch (GenericConfigException e) {
      Debug.logError(e, "Could not get resource URL", module);
    }

    // utilTimer.timerString("  After createModelEntity -- " + i + " --");
    if (modelEntity != null) {
      modelEntity.setLocation(resourceLocation);
      // utilTimer.timerString("  After entityCache.put -- " + i + " --");
      if (isEntity) {
        if (Debug.verboseOn()) Debug.logVerbose("-- [Entity]: #" + i + ": " + entityName, module);
      } else {
        if (Debug.verboseOn())
          Debug.logVerbose("-- [ViewEntity]: #" + i + ": " + entityName, module);
      }
    } else {
      Debug.logWarning(
          "-- -- ENTITYGEN ERROR:getModelEntity: Could not create "
              + "entity for entityName: "
              + entityName,
          module);
    }
    return modelEntity;
  }

  public Map<String, ModelEntity> getEntityCache() throws GenericEntityException {
    if (entityCache == null) { // don't want to block here
      synchronized (ModelReader.class) {
        // must check if null again as one of the blocked threads can still enter
        if (entityCache == null) { // now it's safe
          numEntities = 0;
          numViewEntities = 0;
          numFields = 0;
          numRelations = 0;
          numAutoRelations = 0;

          entityCache = new HashMap<String, ModelEntity>();
          List<ModelViewEntity> tempViewEntityList = FastList.newInstance();
          List<Element> tempExtendEntityElementList = FastList.newInstance();

          UtilTimer utilTimer = new UtilTimer();

          for (ResourceHandler entityResourceHandler : entityResourceHandlers) {

            // utilTimer.timerString("Before getDocument in file " + entityFileName);
            Document document = null;

            try {
              document = entityResourceHandler.getDocument();
            } catch (GenericConfigException e) {
              throw new GenericEntityConfException(
                  "Error getting document from resource handler", e);
            }
            if (document == null) {
              throw new GenericEntityConfException(
                  "Could not get document for " + entityResourceHandler.toString());
            }

            // utilTimer.timerString("Before getDocumentElement in " +
            // entityResourceHandler.toString());
            Element docElement = document.getDocumentElement();

            if (docElement == null) {
              return null;
            }
            docElement.normalize();
            Node curChild = docElement.getFirstChild();

            ModelInfo def = new ModelInfo();
            def.populateFromElements(docElement);
            int i = 0;

            if (curChild != null) {
              utilTimer.timerString(
                  "Before start of entity loop in " + entityResourceHandler.toString());
              do {
                boolean isEntity = "entity".equals(curChild.getNodeName());
                boolean isViewEntity = "view-entity".equals(curChild.getNodeName());
                boolean isExtendEntity = "extend-entity".equals(curChild.getNodeName());

                if ((isEntity || isViewEntity) && curChild.getNodeType() == Node.ELEMENT_NODE) {
                  i++;
                  ModelEntity modelEntity =
                      buildEntity(entityResourceHandler, (Element) curChild, i, def);
                  // put the view entity in a list to get ready for the second pass to populate
                  // fields...
                  if (isViewEntity) {
                    tempViewEntityList.add((ModelViewEntity) modelEntity);
                  } else {
                    entityCache.put(modelEntity.getEntityName(), modelEntity);
                  }
                } else if (isExtendEntity && curChild.getNodeType() == Node.ELEMENT_NODE) {
                  tempExtendEntityElementList.add((Element) curChild);
                }
              } while ((curChild = curChild.getNextSibling()) != null);
            } else {
              Debug.logWarning("No child nodes found.", module);
            }
            utilTimer.timerString(
                "Finished "
                    + entityResourceHandler.toString()
                    + " - Total Entities: "
                    + i
                    + " FINISHED");
          }

          // all entity elements in, now go through extend-entity elements and add their stuff
          for (Element extendEntityElement : tempExtendEntityElementList) {
            String entityName = UtilXml.checkEmpty(extendEntityElement.getAttribute("entity-name"));
            ModelEntity modelEntity = entityCache.get(entityName);
            if (modelEntity == null)
              throw new GenericEntityConfException(
                  "Entity to extend does not exist: " + entityName);
            modelEntity.addExtendEntity(this, extendEntityElement);
          }

          // do a pass on all of the view entities now that all of the entities have
          // loaded and populate the fields
          while (!tempViewEntityList.isEmpty()) {
            int startSize = tempViewEntityList.size();
            Iterator<ModelViewEntity> mveIt = tempViewEntityList.iterator();
            TEMP_VIEW_LOOP:
            while (mveIt.hasNext()) {
              ModelViewEntity curViewEntity = mveIt.next();
              for (ModelViewEntity.ModelMemberEntity mve :
                  curViewEntity.getAllModelMemberEntities()) {
                if (!entityCache.containsKey(mve.getEntityName())) {
                  continue TEMP_VIEW_LOOP;
                }
              }
              mveIt.remove();
              curViewEntity.populateFields(this);
              for (ModelViewEntity.ModelMemberEntity mve :
                  curViewEntity.getAllModelMemberEntities()) {
                ModelEntity me = (ModelEntity) entityCache.get(mve.getEntityName());
                me.addViewEntity(curViewEntity);
              }
              entityCache.put(curViewEntity.getEntityName(), curViewEntity);
            }
            if (tempViewEntityList.size() == startSize) {
              // Oops, the remaining views reference other entities
              // that can't be found, or they reference other views
              // that have some reference problem.
              break;
            }
          }
          if (!tempViewEntityList.isEmpty()) {
            StringBuilder sb = new StringBuilder("View entities reference non-existant members:\n");
            Set<String> allViews = FastSet.newInstance();
            for (ModelViewEntity curViewEntity : tempViewEntityList) {
              allViews.add(curViewEntity.getEntityName());
            }
            for (ModelViewEntity curViewEntity : tempViewEntityList) {
              Set<String> perViewMissingEntities = FastSet.newInstance();
              Iterator<ModelViewEntity.ModelMemberEntity> mmeIt =
                  curViewEntity.getAllModelMemberEntities().iterator();
              while (mmeIt.hasNext()) {
                ModelViewEntity.ModelMemberEntity mme = mmeIt.next();
                String memberEntityName = mme.getEntityName();
                if (!entityCache.containsKey(memberEntityName)) {
                  // this member is not a real entity
                  // check to see if it is a view
                  if (!allViews.contains(memberEntityName)) {
                    // not a view, it's a real missing entity
                    perViewMissingEntities.add(memberEntityName);
                  }
                }
              }
              for (String perViewMissingEntity : perViewMissingEntities) {
                sb.append("\t[")
                    .append(curViewEntity.getEntityName())
                    .append("] missing member entity [")
                    .append(perViewMissingEntity)
                    .append("]\n");
              }
            }
            throw new GenericEntityConfException(sb.toString());
          }

          // auto-create relationships
          TreeSet<String> orderedMessages = new TreeSet<String>();
          for (String curEntityName : new TreeSet<String>(this.getEntityNames())) {
            ModelEntity curModelEntity = this.getModelEntity(curEntityName);
            if (curModelEntity instanceof ModelViewEntity) {
              // for view-entities auto-create relationships for all member-entity relationships
              // that have all corresponding fields in the view-entity

            } else {
              // for entities auto-create many relationships for all type one relationships

              // just in case we add a new relation to the same entity, keep in a separate list and
              // add them at the end
              List<ModelRelation> newSameEntityRelations = FastList.newInstance();

              Iterator<ModelRelation> relationsIter = curModelEntity.getRelationsIterator();
              while (relationsIter.hasNext()) {
                ModelRelation modelRelation = relationsIter.next();
                if (("one".equals(modelRelation.getType())
                        || "one-nofk".equals(modelRelation.getType()))
                    && !modelRelation.isAutoRelation()) {
                  ModelEntity relatedEnt = null;
                  try {
                    relatedEnt = this.getModelEntity(modelRelation.getRelEntityName());
                  } catch (GenericModelException e) {
                    throw new GenericModelException(
                        "Error getting related entity ["
                            + modelRelation.getRelEntityName()
                            + "] definition from entity ["
                            + curEntityName
                            + "]",
                        e);
                  }
                  if (relatedEnt != null) {
                    // don't do relationship to the same entity, unless title is "Parent", then do a
                    // "Child" automatically
                    String targetTitle = modelRelation.getTitle();
                    if (curModelEntity.getEntityName().equals(relatedEnt.getEntityName())
                        && "Parent".equals(targetTitle)) {
                      targetTitle = "Child";
                    }

                    // create the new relationship even if one exists so we can show what we are
                    // looking for in the info message
                    ModelRelation newRel = new ModelRelation();
                    newRel.setModelEntity(relatedEnt);
                    newRel.setRelEntityName(curModelEntity.getEntityName());
                    newRel.setTitle(targetTitle);
                    newRel.setAutoRelation(true);
                    Set<String> curEntityKeyFields = FastSet.newInstance();
                    for (int kmn = 0; kmn < modelRelation.getKeyMapsSize(); kmn++) {
                      ModelKeyMap curkm = modelRelation.getKeyMap(kmn);
                      ModelKeyMap newkm = new ModelKeyMap();
                      newRel.addKeyMap(newkm);
                      newkm.setFieldName(curkm.getRelFieldName());
                      newkm.setRelFieldName(curkm.getFieldName());
                      curEntityKeyFields.add(curkm.getFieldName());
                    }
                    // decide whether it should be one or many by seeing if the key map represents
                    // the complete pk of the relEntity
                    if (curModelEntity.containsAllPkFieldNames(curEntityKeyFields)) {
                      // always use one-nofk, we don't want auto-fks getting in for these automatic
                      // ones
                      newRel.setType("one-nofk");

                      // to keep it clean, remove any additional keys that aren't part of the PK
                      List<String> curPkFieldNames = curModelEntity.getPkFieldNames();
                      Iterator<ModelKeyMap> nrkmIter = newRel.getKeyMapsIterator();
                      while (nrkmIter.hasNext()) {
                        ModelKeyMap nrkm = nrkmIter.next();
                        String checkField = nrkm.getRelFieldName();
                        if (!curPkFieldNames.contains(checkField)) {
                          nrkmIter.remove();
                        }
                      }
                    } else {
                      newRel.setType("many");
                    }

                    ModelRelation existingRelation =
                        relatedEnt.getRelation(targetTitle + curModelEntity.getEntityName());
                    if (existingRelation == null) {
                      numAutoRelations++;
                      if (curModelEntity.getEntityName().equals(relatedEnt.getEntityName())) {
                        newSameEntityRelations.add(newRel);
                      } else {
                        relatedEnt.addRelation(newRel);
                      }
                    } else {
                      if (newRel.equals(existingRelation)) {
                        // don't warn if the target title+entity = current title+entity
                        if (!(targetTitle + curModelEntity.getEntityName())
                            .equals(modelRelation.getTitle() + modelRelation.getRelEntityName())) {
                          // String errorMsg = "Relation already exists to entity [] with title [" +
                          // targetTitle + "],from entity []";
                          String message =
                              "Entity ["
                                  + relatedEnt.getPackageName()
                                  + ":"
                                  + relatedEnt.getEntityName()
                                  + "] already has identical relationship to entity ["
                                  + curModelEntity.getEntityName()
                                  + "] title ["
                                  + targetTitle
                                  + "]; would auto-create: type ["
                                  + newRel.getType()
                                  + "] and fields ["
                                  + newRel.keyMapString(",", "")
                                  + "]";
                          orderedMessages.add(message);
                        }
                      } else {
                        String message =
                            "Existing relationship with the same name, but different specs found from what would be auto-created for Entity ["
                                + relatedEnt.getEntityName()
                                + "] and relationship to entity ["
                                + curModelEntity.getEntityName()
                                + "] title ["
                                + targetTitle
                                + "]; would auto-create: type ["
                                + newRel.getType()
                                + "] and fields ["
                                + newRel.keyMapString(",", "")
                                + "]";
                        Debug.logVerbose(message, module);
                      }
                    }
                  } else {
                    String errorMsg =
                        "Could not find related entity ["
                            + modelRelation.getRelEntityName()
                            + "], no reverse relation added.";
                    Debug.logWarning(errorMsg, module);
                  }
                }
              }

              if (newSameEntityRelations.size() > 0) {
                for (ModelRelation newRel : newSameEntityRelations) {
                  curModelEntity.addRelation(newRel);
                }
              }
            }
          }

          for (String message : orderedMessages) {
            Debug.logInfo(message, module);
          }

          Debug.log(
              "FINISHED LOADING ENTITIES - ALL FILES; #Entities="
                  + numEntities
                  + " #ViewEntities="
                  + numViewEntities
                  + " #Fields="
                  + numFields
                  + " #Relationships="
                  + numRelations
                  + " #AutoRelationships="
                  + numAutoRelations,
              module);
        }
      }
    }
    return entityCache;
  }

  /**
   * rebuilds the resourceHandlerEntities Map of Collections based on the current
   * entityResourceHandlerMap Map, must be done whenever a manual change is made to the
   * entityResourceHandlerMap Map after the initial load to make them consistent again.
   */
  public void rebuildResourceHandlerEntities() {
    resourceHandlerEntities = FastMap.newInstance();
    Iterator<Map.Entry<String, ResourceHandler>> entityResourceIter =
        entityResourceHandlerMap.entrySet().iterator();

    while (entityResourceIter.hasNext()) {
      Map.Entry<String, ResourceHandler> entry = entityResourceIter.next();
      // add entityName to appropriate resourceHandlerEntities collection
      Collection<String> resourceHandlerEntityNames = resourceHandlerEntities.get(entry.getValue());

      if (resourceHandlerEntityNames == null) {
        resourceHandlerEntityNames = FastList.newInstance();
        resourceHandlerEntities.put(entry.getValue(), resourceHandlerEntityNames);
      }
      resourceHandlerEntityNames.add(entry.getKey());
    }
  }

  public Iterator<ResourceHandler> getResourceHandlerEntitiesKeyIterator() {
    if (resourceHandlerEntities == null) return null;
    return resourceHandlerEntities.keySet().iterator();
  }

  public Collection<String> getResourceHandlerEntities(ResourceHandler resourceHandler) {
    if (resourceHandlerEntities == null) return null;
    return resourceHandlerEntities.get(resourceHandler);
  }

  public void addEntityToResourceHandler(String entityName, String loaderName, String location) {
    entityResourceHandlerMap.put(
        entityName,
        new MainResourceHandler(EntityConfigUtil.ENTITY_ENGINE_XML_FILENAME, loaderName, location));
  }

  public ResourceHandler getEntityResourceHandler(String entityName) {
    return entityResourceHandlerMap.get(entityName);
  }

  /**
   * Gets an Entity object based on a definition from the specified XML Entity descriptor file.
   *
   * @param entityName The entityName of the Entity definition to use.
   * @return An Entity object describing the specified entity of the specified descriptor file.
   */
  public ModelEntity getModelEntity(String entityName) throws GenericEntityException {
    if (entityName == null) {
      throw new IllegalArgumentException("Tried to find entity definition for a null entityName");
    }
    Map<String, ModelEntity> ec = getEntityCache();
    if (ec == null) {
      throw new GenericEntityConfException("ERROR: Unable to load Entity Cache");
    }
    ModelEntity modelEntity = ec.get(entityName);
    if (modelEntity == null) {
      String errMsg = "Could not find definition for entity name " + entityName;
      // Debug.logError(new Exception("Placeholder"), errMsg, module);
      throw new GenericModelException(errMsg);
    }
    return modelEntity;
  }

  public ModelEntity getModelEntityNoCheck(String entityName) {
    Map<String, ModelEntity> ec = null;
    try {
      ec = getEntityCache();
    } catch (GenericEntityException e) {
      Debug.logError(e, "Error getting entity cache", module);
    }
    if (ec == null) {
      return null;
    }
    ModelEntity modelEntity = ec.get(entityName);
    return modelEntity;
  }

  /**
   * Creates a Iterator with the entityName of each Entity defined in the specified XML Entity
   * Descriptor file.
   *
   * @return A Iterator of entityName Strings
   */
  public Iterator<String> getEntityNamesIterator() throws GenericEntityException {
    Collection<String> collection = getEntityNames();
    if (collection != null) {
      return collection.iterator();
    } else {
      return null;
    }
  }

  /**
   * Creates a Set with the entityName of each Entity defined in the specified XML Entity Descriptor
   * file.
   *
   * @return A Set of entityName Strings
   */
  public Set<String> getEntityNames() throws GenericEntityException {
    Map<String, ModelEntity> ec = getEntityCache();
    if (ec == null) {
      throw new GenericEntityConfException("ERROR: Unable to load Entity Cache");
    }
    return ec.keySet();
  }

  /** Get all entities, organized by package */
  public Map<String, TreeSet<String>> getEntitiesByPackage(
      Set<String> packageFilterSet, Set<String> entityFilterSet) throws GenericEntityException {
    Map<String, TreeSet<String>> entitiesByPackage = FastMap.newInstance();

    // put the entityNames TreeSets in a HashMap by packageName
    Iterator<String> ecIter = this.getEntityNames().iterator();
    while (ecIter.hasNext()) {
      String entityName = ecIter.next();
      ModelEntity entity = this.getModelEntity(entityName);
      String packageName = entity.getPackageName();

      if (UtilValidate.isNotEmpty(packageFilterSet)) {
        // does it match any of these?
        boolean foundMatch = false;
        for (String packageFilter : packageFilterSet) {
          if (packageName.contains(packageFilter)) {
            foundMatch = true;
          }
        }
        if (!foundMatch) {
          // Debug.logInfo("Not including entity " + entityName + " becuase it is not in the package
          // set: " + packageFilterSet, module);
          continue;
        }
      }
      if (UtilValidate.isNotEmpty(entityFilterSet) && !entityFilterSet.contains(entityName)) {
        // Debug.logInfo("Not including entity " + entityName + " becuase it is not in the entity
        // set: " + entityFilterSet, module);
        continue;
      }

      TreeSet<String> entities = entitiesByPackage.get(entity.getPackageName());
      if (entities == null) {
        entities = new TreeSet<String>();
        entitiesByPackage.put(entity.getPackageName(), entities);
      }
      entities.add(entityName);
    }

    return entitiesByPackage;
  }

  /**
   * Util method to validate an entity name; if no entity is found with the name, characters are
   * stripped from the beginning of the name until a valid entity name is found. It is intended to
   * be used to determine the entity name from a relation name.
   *
   * @return A valid entityName or null
   */
  public String validateEntityName(String entityName) throws GenericEntityException {
    if (entityName == null) {
      return null;
    }
    Set<String> allEntities = this.getEntityNames();
    while (!allEntities.contains(entityName) && entityName.length() > 0) {
      entityName = entityName.substring(1);
    }
    return (entityName.length() > 0 ? entityName : null);
  }

  ModelEntity createModelEntity(Element entityElement, UtilTimer utilTimer, ModelInfo def) {
    if (entityElement == null) return null;
    this.numEntities++;
    ModelEntity entity = new ModelEntity(this, entityElement, utilTimer, def);
    return entity;
  }

  ModelEntity createModelViewEntity(Element entityElement, UtilTimer utilTimer, ModelInfo def) {
    if (entityElement == null) return null;
    this.numViewEntities++;
    ModelViewEntity entity = new ModelViewEntity(this, entityElement, utilTimer, def);
    return entity;
  }

  public ModelRelation createRelation(ModelEntity entity, Element relationElement) {
    this.numRelations++;
    ModelRelation relation = new ModelRelation(entity, relationElement);
    return relation;
  }

  public ModelUniqueKey createUniqueKey(ModelEntity entity, Element uniqueKeyElement) {
    this.numUniqueKeys++;
    ModelUniqueKey relation = new ModelUniqueKey(entity, uniqueKeyElement);
    return relation;
  }

  public ModelField findModelField(ModelEntity entity, String fieldName) {
    for (ModelField field : entity.fields) {
      if (field.name.compareTo(fieldName) == 0) {
        return field;
      }
    }
    return null;
  }

  public ModelField createModelField(String name, String type, String colName, boolean isPk) {
    this.numFields++;
    ModelField field = new ModelField(name, type, colName, isPk);
    return field;
  }

  public ModelField createModelField(Element fieldElement) {
    if (fieldElement == null) {
      return null;
    }

    this.numFields++;
    ModelField field = new ModelField(fieldElement);
    return field;
  }
}
/** Widget Library - Screen model HTML class. */
@SuppressWarnings("serial")
public class HtmlWidget extends ModelScreenWidget {
  public static final String module = HtmlWidget.class.getName();

  public static UtilCache<String, Template> specialTemplateCache =
      UtilCache.createUtilCache("widget.screen.template.ftl.general", 0, 0, false);
  protected static BeansWrapper specialBeansWrapper = new ExtendedWrapper();
  protected static Configuration specialConfig =
      FreeMarkerWorker.makeConfiguration(specialBeansWrapper);

  // not sure if this is the best way to get FTL to use my fancy MapModel derivative, but should
  // work at least...
  public static class ExtendedWrapper extends BeansWrapper {
    @Override
    public TemplateModel wrap(Object object) throws TemplateModelException {
      /* NOTE: don't use this and the StringHtmlWrapperForFtl or things will be double-encoded
      if (object instanceof GenericValue) {
          return new GenericValueHtmlWrapperForFtl((GenericValue) object, this);
      }*/
      // This StringHtmlWrapperForFtl option seems to be the best option
      // and handles most things without causing too many problems
      if (object instanceof String) {
        return new StringHtmlWrapperForFtl((String) object, this);
      }
      return super.wrap(object);
    }
  }

  public static class StringHtmlWrapperForFtl extends StringModel {
    public StringHtmlWrapperForFtl(String str, BeansWrapper wrapper) {
      super(str, wrapper);
    }

    @Override
    public String getAsString() {
      return StringUtil.htmlEncoder.encode(super.getAsString());
    }
  }

  // End Static, begin class section

  protected List<ModelScreenWidget> subWidgets = new ArrayList<ModelScreenWidget>();

  public HtmlWidget(ModelScreen modelScreen, Element htmlElement) {
    super(modelScreen, htmlElement);
    List<? extends Element> childElementList = UtilXml.childElementList(htmlElement);
    for (Element childElement : childElementList) {
      if ("html-template".equals(childElement.getNodeName())) {
        this.subWidgets.add(new HtmlTemplate(modelScreen, childElement));
      } else if ("html-template-decorator".equals(childElement.getNodeName())) {
        this.subWidgets.add(new HtmlTemplateDecorator(modelScreen, childElement));
      } else {
        throw new IllegalArgumentException(
            "Tag not supported under the platform-specific -> html tag with name: "
                + childElement.getNodeName());
      }
    }
  }

  @Override
  public void renderWidgetString(
      Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer)
      throws GeneralException, IOException {
    for (ModelScreenWidget subWidget : subWidgets) {
      subWidget.renderWidgetString(writer, context, screenStringRenderer);
    }
  }

  @Override
  public String rawString() {
    StringBuilder buffer = new StringBuilder("<html-widget>");
    for (ModelScreenWidget subWidget : subWidgets) {
      buffer.append(subWidget.rawString());
    }
    buffer.append("</html-widget>");
    return buffer.toString();
  }

  public static void renderHtmlTemplate(
      Appendable writer, FlexibleStringExpander locationExdr, Map<String, Object> context) {
    String location = locationExdr.expandString(context);
    // Debug.logInfo("Rendering template at location [" + location + "] with context: \n" + context,
    // module);

    if (UtilValidate.isEmpty(location)) {
      throw new IllegalArgumentException("Template location is empty");
    }

    /*
    // =======================================================================
    // Go through the context and find GenericValue objects and wrap them

    // NOTE PROBLEM: there are still problems with this as it gets some things
    // but does not get non-entity data including lots of strings
    // directly in the context or things prepared or derived right in
    // the FTL file, like the results of service calls, etc; we could
    // do something more aggressive to encode and wrap EVERYTHING in
    // the context, but I've been thinking that even this is too much
    // overhead and that would be crazy

    // NOTE ALTERNATIVE1: considering instead to use the FTL features to wrap
    // everything in an <#escape x as x?html>...</#escape>, but that could
    // cause problems with ${} expansions that have HTML in them, including:
    // included screens (using ${screens.render(...)}), content that should
    // have HTML in it (lots of general, product, category, etc content), etc

    // NOTE ALTERNATIVE2: kind of like the "#escape X as x?html" option,
    // implement an FTL *Model class and load it through a ObjectWrapper
    // FINAL NOTE: after testing all of these alternatives, this one seems
    // to behave the best, so going with that for now.

    // isolate the scope so these wrapper objects go away after rendering is done
    MapStack<String> contextMs;
    if (!(context instanceof MapStack)) {
        contextMs = MapStack.create(context);
        context = contextMs;
    } else {
        contextMs = UtilGenerics.cast(context);
    }

    contextMs.push();
    for(Map.Entry<String, Object> mapEntry: contextMs.entrySet()) {
        Object value = mapEntry.getValue();
        if (value instanceof GenericValue) {
            contextMs.put(mapEntry.getKey(), GenericValueHtmlWrapper.create((GenericValue) value));
        } else if (value instanceof List) {
            if (((List) value).size() > 0 && ((List) value).get(0) instanceof GenericValue) {
                List<GenericValue> theList = (List<GenericValue>) value;
                List<GenericValueHtmlWrapper> newList = FastList.newInstance();
                for (GenericValue gv: theList) {
                    newList.add(GenericValueHtmlWrapper.create(gv));
                }
                contextMs.put(mapEntry.getKey(), newList);
            }
        }
        // TODO and NOTE: should get most stuff, but we could support Maps
        // and Lists in Maps and such; that's tricky because we have to go
        // through the entire Map and not just one entry, and we would
        // have to shallow copy the whole Map too

    }
    // this line goes at the end of the method, but moved up here to be part of the big comment about this
    contextMs.pop();
     */

    if (location.endsWith(".ftl")) {
      try {
        Map<String, ? extends Object> parameters = UtilGenerics.checkMap(context.get("parameters"));
        boolean insertWidgetBoundaryComments =
            ModelWidget.widgetBoundaryCommentsEnabled(parameters);
        if (insertWidgetBoundaryComments) {
          writer.append(HtmlWidgetRenderer.formatBoundaryComment("Begin", "Template", location));
        }

        // FreeMarkerWorker.renderTemplateAtLocation(location, context, writer);
        Template template = null;
        if (location.endsWith(".fo.ftl")) { // FOP can't render correctly escaped characters
          template = FreeMarkerWorker.getTemplate(location);
        } else {
          template = FreeMarkerWorker.getTemplate(location, specialTemplateCache, specialConfig);
        }
        FreeMarkerWorker.renderTemplate(template, context, writer);

        if (insertWidgetBoundaryComments) {
          writer.append(HtmlWidgetRenderer.formatBoundaryComment("End", "Template", location));
        }
      } catch (IllegalArgumentException e) {
        String errMsg =
            "Error rendering included template at location [" + location + "]: " + e.toString();
        Debug.logError(e, errMsg, module);
        writeError(writer, errMsg);
      } catch (MalformedURLException e) {
        String errMsg =
            "Error rendering included template at location [" + location + "]: " + e.toString();
        Debug.logError(e, errMsg, module);
        writeError(writer, errMsg);
      } catch (TemplateException e) {
        String errMsg =
            "Error rendering included template at location [" + location + "]: " + e.toString();
        Debug.logError(e, errMsg, module);
        writeError(writer, errMsg);
      } catch (IOException e) {
        String errMsg =
            "Error rendering included template at location [" + location + "]: " + e.toString();
        Debug.logError(e, errMsg, module);
        writeError(writer, errMsg);
      }
    } else {
      throw new IllegalArgumentException(
          "Rendering not yet supported for the template at location: " + location);
    }
  }

  // TODO: We can make this more fancy, but for now this is very functional
  public static void writeError(Appendable writer, String message) {
    try {
      writer.append(message);
    } catch (IOException e) {
    }
  }

  public static class HtmlTemplate extends ModelScreenWidget {
    protected FlexibleStringExpander locationExdr;

    public HtmlTemplate(ModelScreen modelScreen, Element htmlTemplateElement) {
      super(modelScreen, htmlTemplateElement);
      this.locationExdr =
          FlexibleStringExpander.getInstance(htmlTemplateElement.getAttribute("location"));
    }

    @Override
    public void renderWidgetString(
        Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) {
      renderHtmlTemplate(writer, this.locationExdr, context);
    }

    @Override
    public String rawString() {
      return "<html-template location=\"" + this.locationExdr.getOriginal() + "\"/>";
    }
  }

  public static class HtmlTemplateDecorator extends ModelScreenWidget {
    protected FlexibleStringExpander locationExdr;
    protected Map<String, HtmlTemplateDecoratorSection> sectionMap = FastMap.newInstance();

    public HtmlTemplateDecorator(ModelScreen modelScreen, Element htmlTemplateDecoratorElement) {
      super(modelScreen, htmlTemplateDecoratorElement);
      this.locationExdr =
          FlexibleStringExpander.getInstance(htmlTemplateDecoratorElement.getAttribute("location"));

      List<? extends Element> htmlTemplateDecoratorSectionElementList =
          UtilXml.childElementList(htmlTemplateDecoratorElement, "html-template-decorator-section");
      for (Element htmlTemplateDecoratorSectionElement : htmlTemplateDecoratorSectionElementList) {
        String name = htmlTemplateDecoratorSectionElement.getAttribute("name");
        this.sectionMap.put(
            name,
            new HtmlTemplateDecoratorSection(modelScreen, htmlTemplateDecoratorSectionElement));
      }
    }

    @Override
    public void renderWidgetString(
        Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) {
      // isolate the scope
      MapStack<String> contextMs;
      if (!(context instanceof MapStack)) {
        contextMs = MapStack.create(context);
        context = contextMs;
      } else {
        contextMs = UtilGenerics.cast(context);
      }

      // create a standAloneStack, basically a "save point" for this SectionsRenderer, and make a
      // new "screens" object just for it so it is isolated and doesn't follow the stack down
      MapStack<String> standAloneStack = contextMs.standAloneChildStack();
      standAloneStack.put(
          "screens", new ScreenRenderer(writer, standAloneStack, screenStringRenderer));
      SectionsRenderer sections =
          new SectionsRenderer(this.sectionMap, standAloneStack, writer, screenStringRenderer);

      // put the sectionMap in the context, make sure it is in the sub-scope, ie after calling push
      // on the MapStack
      contextMs.push();
      context.put("sections", sections);

      renderHtmlTemplate(writer, this.locationExdr, context);
      contextMs.pop();
    }

    @Override
    public String rawString() {
      return "<html-template-decorator location=\"" + this.locationExdr.getOriginal() + "\"/>";
    }
  }

  public static class HtmlTemplateDecoratorSection extends ModelScreenWidget {
    protected String name;
    protected List<ModelScreenWidget> subWidgets;

    public HtmlTemplateDecoratorSection(
        ModelScreen modelScreen, Element htmlTemplateDecoratorSectionElement) {
      super(modelScreen, htmlTemplateDecoratorSectionElement);
      this.name = htmlTemplateDecoratorSectionElement.getAttribute("name");
      // read sub-widgets
      List<? extends Element> subElementList =
          UtilXml.childElementList(htmlTemplateDecoratorSectionElement);
      this.subWidgets = ModelScreenWidget.readSubWidgets(this.modelScreen, subElementList);
    }

    @Override
    public void renderWidgetString(
        Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer)
        throws GeneralException, IOException {
      // render sub-widgets
      renderSubWidgetsString(this.subWidgets, writer, context, screenStringRenderer);
    }

    @Override
    public String rawString() {
      return "<html-template-decorator-section name=\"" + this.name + "\"/>";
    }
  }
}
/** Performs regular expression matching and compiled regular expression pattern caching. */
public class CompilerMatcher {

  public static final String module = CompilerMatcher.class.getName();

  private static final UtilCache<String, Pattern> compiledPatterns =
      UtilCache.createUtilCache("regularExpression.compiledPatterns", false);

  private Perl5Compiler compiler = new Perl5Compiler();
  private Perl5Matcher matcher = new Perl5Matcher();
  private Perl5Util perl5Util = new Perl5Util();

  /**
   * This class is *not* thread-safe so it must be accessed from a java.lang.ThreadLocal instance
   * for multi-thread usage. ThreadLocal causes slightly extra memory usage, but allows for faster
   * thread-safe processing than synchronization would afford.
   *
   * @return returns the ThreadLocal
   */
  public static ThreadLocal<CompilerMatcher> getThreadLocal() {
    return new ThreadLocal<CompilerMatcher>() {

      @Override
      protected CompilerMatcher initialValue() {
        return new CompilerMatcher();
      }
    };
  }

  /**
   * Returns true if the compiled version of the patternString regular expression argument matches
   * the aString argument. Case sensitive
   *
   * @param aString a string
   * @param patternString a string pattern
   * @return returns true if the compiled version of the patternString regular expression argument
   *     matches the aString argument
   * @throws MalformedPatternException
   */
  public boolean matches(String aString, String patternString) throws MalformedPatternException {
    return this.matches(aString, patternString, true);
  }

  /**
   * Returns true if the compiled version of the patternString regular expression argument matches
   * the aString argument.
   *
   * @param aString a string
   * @param patternString a string pattern
   * @param caseSensitive case sensitive true/false
   * @return returns true if the compiled version of the patternString regular expression argument
   *     matches the aString argument
   * @throws MalformedPatternException
   */
  public boolean matches(String aString, String patternString, boolean caseSensitive)
      throws MalformedPatternException {
    return this.matcher.matches(aString, this.getTestPattern(patternString, caseSensitive));
  }

  /**
   * Returns true if the compiled version of the patternString regular expression argument is
   * contained in the aString argument.
   *
   * @param aString a string
   * @param patternString a pattern string
   * @return Returns true if the compiled version of the patternString regular expression argument
   *     is contained in the aString argument
   * @throws MalformedPatternException
   */
  public boolean contains(String aString, String patternString) throws MalformedPatternException {
    return this.matcher.contains(aString, this.getTestPattern(patternString));
  }

  /**
   * Compiles and caches a case sensitive regexp pattern for the given string pattern.
   *
   * @param stringPattern a pattern string
   * @return compiles and caches a case sensitive regexp pattern for the given string pattern
   * @throws MalformedPatternException
   */
  private Pattern getTestPattern(String stringPattern) throws MalformedPatternException {
    return this.getTestPattern(stringPattern, true);
  }

  /**
   * Compiles and caches a regexp pattern for the given string pattern.
   *
   * @param stringPattern a pattern string
   * @param caseSensitive case sensitive true/false
   * @return compiles and caches a regexp pattern for the given string pattern
   * @throws MalformedPatternException
   */
  private Pattern getTestPattern(String stringPattern, boolean caseSensitive)
      throws MalformedPatternException {
    Pattern pattern = compiledPatterns.get(stringPattern);
    if (pattern == null) {
      if (caseSensitive) {
        pattern = compiler.compile(stringPattern);
      } else {
        pattern = compiler.compile(stringPattern, Perl5Compiler.CASE_INSENSITIVE_MASK);
      }

      compiledPatterns.put(stringPattern, pattern);
      Debug.logVerbose(
          "Compiled and cached a pattern: '" + stringPattern + "' - " + Thread.currentThread(),
          module);
    }
    return pattern;
  }

  /**
   * Perl5Util's substitute() function implements Perl's s/// operator. It takes two arguments: a
   * substitution expression, and an input.
   *
   * @param stringPattern a pattern string
   * @param input the input string
   * @return returns the perl5Util's substitute() function implements Perl's
   */
  public String substitute(String stringPattern, String input) {
    return this.perl5Util.substitute(stringPattern, input);
  }
}
/**
 * Expands String values that contain Unified Expression Language (JSR 245) syntax. This class also
 * supports the execution of bsh scripts by using the 'bsh:' prefix, and Groovy scripts by using the
 * 'groovy:' prefix. Further it is possible to control the output by specifying the suffix
 * '?currency(XXX)' to format the output according to the supplied locale and specified (XXX)
 * currency.
 *
 * <p>This class extends the UEL by allowing nested expressions.
 */
@SourceMonitored
@SuppressWarnings("serial")
public abstract class FlexibleStringExpander implements Serializable {

  public static final String module = FlexibleStringExpander.class.getName();
  public static final String openBracket = "${";
  public static final String closeBracket = "}";
  protected static final UtilCache<Key, FlexibleStringExpander> exprCache =
      UtilCache.createUtilCache("flexibleStringExpander.ExpressionCache");
  protected static final FlexibleStringExpander nullExpr = new ConstSimpleElem(new char[0]);

  /**
   * Evaluate an expression and return the result as a <code>String</code>. Null expressions return
   * <code>null</code>. A null <code>context</code> argument will return the original expression.
   *
   * <p>Note that the behavior of this method is not the same as using <code>
   * FlexibleStringExpander.getInstance(expression).expandString(context)</code> because it returns
   * <code>null</code> when given a null <code>expression</code> argument, and <code>
   * FlexibleStringExpander.getInstance(expression).expandString(context)</code> returns an empty
   * <code>String</code>.
   *
   * @param expression The original expression
   * @param context The evaluation context
   * @return The original expression's evaluation result as a <code>String</code>
   */
  public static String expandString(String expression, Map<String, ? extends Object> context) {
    return expandString(expression, context, null, null);
  }

  /**
   * Evaluate an expression and return the result as a <code>String</code>. Null expressions return
   * <code>null</code>. A null <code>context</code> argument will return the original expression.
   *
   * <p>Note that the behavior of this method is not the same as using <code>
   * FlexibleStringExpander.getInstance(expression).expandString(context, locale)</code> because it
   * returns <code>null</code> when given a null <code>expression</code> argument, and <code>
   * FlexibleStringExpander.getInstance(expression).expandString(context, locale)</code> returns an
   * empty <code>String</code>.
   *
   * @param expression The original expression
   * @param context The evaluation context
   * @param locale The locale to be used for localization
   * @return The original expression's evaluation result as a <code>String</code>
   */
  public static String expandString(
      String expression, Map<String, ? extends Object> context, Locale locale) {
    return expandString(expression, context, null, locale);
  }

  /**
   * Evaluate an expression and return the result as a <code>String</code>. Null expressions return
   * <code>null</code>. A null <code>context</code> argument will return the original expression.
   *
   * <p>Note that the behavior of this method is not the same as using <code>
   * FlexibleStringExpander.getInstance(expression).expandString(context, timeZone locale)</code>
   * because it returns <code>null</code> when given a null <code>expression</code> argument, and
   * <code>FlexibleStringExpander.getInstance(expression).expandString(context, timeZone, locale)
   * </code> returns an empty <code>String</code>.
   *
   * @param expression The original expression
   * @param context The evaluation context
   * @param timeZone The time zone to be used for localization
   * @param locale The locale to be used for localization
   * @return The original expression's evaluation result as a <code>String</code>
   */
  public static String expandString(
      String expression, Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) {
    if (expression == null) {
      return "";
    }
    if (context == null || !expression.contains(openBracket)) {
      return expression;
    }
    FlexibleStringExpander fse = FlexibleStringExpander.getInstance(expression);
    return fse.expandString(context, timeZone, locale);
  }

  /**
   * Returns a <code>FlexibleStringExpander</code> object.
   *
   * <p>A null or empty argument will return a <code>FlexibleStringExpander</code> object that
   * represents an empty expression. That object is a shared singleton, so there is no memory or
   * performance penalty in using it.
   *
   * <p>If the method is passed a <code>String</code> argument that doesn't contain an expression,
   * the <code>FlexibleStringExpander</code> object that is returned does not perform any
   * evaluations on the original <code>String</code> - any methods that return a <code>String</code>
   * will return the original <code>String</code>. The object returned by this method is very
   * compact - taking less memory than the original <code>String</code>.
   *
   * @param expression The original expression
   * @return A <code>FlexibleStringExpander</code> instance
   */
  public static FlexibleStringExpander getInstance(String expression) {
    return getInstance(expression, true);
  }

  /* Returns a <code>FlexibleStringExpander</code> object. <p>A null or
   * empty argument will return a <code>FlexibleStringExpander</code>
   * object that represents an empty expression. That object is a shared
   * singleton, so there is no memory or performance penalty in using it.</p>
   * <p>If the method is passed a <code>String</code> argument that doesn't
   * contain an expression, the <code>FlexibleStringExpander</code> object
   * that is returned does not perform any evaluations on the original
   * <code>String</code> - any methods that return a <code>String</code>
   * will return the original <code>String</code>. The object returned by
   * this method is very compact - taking less memory than the original
   * <code>String</code>.</p>
   *
   * @param expression The original expression
   * @param useCache whether to store things into a global cache
   * @return A <code>FlexibleStringExpander</code> instance
   */
  public static FlexibleStringExpander getInstance(String expression, boolean useCache) {
    if (UtilValidate.isEmpty(expression)) {
      return nullExpr;
    }
    return getInstance(expression, expression.toCharArray(), 0, expression.length(), useCache);
  }

  private static FlexibleStringExpander getInstance(
      String expression, char[] chars, int offset, int length, boolean useCache) {
    if (length == 0) {
      return nullExpr;
    }
    if (!useCache) {
      return parse(chars, offset, length);
    }
    // Remove the next nine lines to cache all expressions
    if (!expression.contains(openBracket)) {
      if (chars.length == length) {
        return new ConstSimpleElem(chars);
      } else {
        return new ConstOffsetElem(chars, offset, length);
      }
    }
    Key key = chars.length == length ? new SimpleKey(chars) : new OffsetKey(chars, offset, length);
    FlexibleStringExpander fse = exprCache.get(key);
    if (fse == null) {
      exprCache.put(key, parse(chars, offset, length));
      fse = exprCache.get(key);
    }
    return fse;
  }

  private abstract static class Key {
    @Override
    public final boolean equals(Object o) {
      // No class test here, nor null, as this class is only used
      // internally
      return toString().equals(o.toString());
    }

    @Override
    public final int hashCode() {
      return toString().hashCode();
    }
  }

  private static final class SimpleKey extends Key {
    private final char[] chars;

    protected SimpleKey(char[] chars) {
      this.chars = chars;
    }

    @Override
    public String toString() {
      return new String(chars);
    }
  }

  private static final class OffsetKey extends Key {
    private final char[] chars;
    private final int offset;
    private final int length;

    protected OffsetKey(char[] chars, int offset, int length) {
      this.chars = chars;
      this.offset = offset;
      this.length = length;
    }

    @Override
    public String toString() {
      return new String(chars, offset, length);
    }
  }

  private static FlexibleStringExpander parse(char[] chars, int offset, int length) {
    FlexibleStringExpander[] strElems = getStrElems(chars, offset, length);
    if (strElems.length == 1) {
      return strElems[0];
    } else {
      return new Elements(chars, offset, length, strElems);
    }
  }

  protected static FlexibleStringExpander[] getStrElems(char[] chars, int offset, int length) {
    String expression = new String(chars, 0, length + offset);
    int start = expression.indexOf(openBracket, offset);
    if (start == -1) {
      return new FlexibleStringExpander[] {new ConstOffsetElem(chars, offset, length)};
    }
    int origLen = length;
    ArrayList<FlexibleStringExpander> strElems = new ArrayList<FlexibleStringExpander>();
    int currentInd = offset;
    int end = -1;
    while (start != -1) {
      end = expression.indexOf(closeBracket, start);
      if (end == -1) {
        Debug.logWarning(
            "Found a ${ without a closing } (curly-brace) in the String: " + expression, module);
        break;
      }
      // Check for escaped expression
      boolean escapedExpression = (start - 1 >= 0 && expression.charAt(start - 1) == '\\');
      if (start > currentInd) {
        // append everything from the current index to the start of the expression
        strElems.add(
            new ConstOffsetElem(
                chars, currentInd, (escapedExpression ? start - 1 : start) - currentInd));
      }
      if (expression.indexOf("bsh:", start + 2) == start + 2 && !escapedExpression) {
        // checks to see if this starts with a "bsh:", if so treat the rest of the expression as a
        // bsh scriptlet
        strElems.add(
            new BshElem(
                chars,
                start,
                Math.min(end + 1, start + length) - start,
                start + 6,
                end - start - 6));
      } else if (expression.indexOf("groovy:", start + 2) == start + 2 && !escapedExpression) {
        // checks to see if this starts with a "groovy:", if so treat the rest of the expression as
        // a groovy scriptlet
        strElems.add(
            new GroovyElem(
                chars,
                start,
                Math.min(end + 1, start + length) - start,
                start + 9,
                end - start - 9));
      } else {
        // Scan for matching closing bracket
        int ptr = expression.indexOf(openBracket, start + 2);
        while (ptr != -1 && end != -1 && ptr < end) {
          end = expression.indexOf(closeBracket, end + 1);
          ptr = expression.indexOf(openBracket, ptr + 2);
        }
        if (end == -1) {
          end = origLen;
        }
        // Evaluation sequence is important - do not change it
        if (escapedExpression) {
          strElems.add(new ConstOffsetElem(chars, start, end + 1 - start));
        } else {
          String subExpression = expression.substring(start + 2, end);
          int currencyPos = subExpression.indexOf("?currency(");
          int closeParen = currencyPos > 0 ? subExpression.indexOf(")", currencyPos + 10) : -1;
          if (closeParen != -1) {
            strElems.add(
                new CurrElem(
                    chars,
                    start,
                    Math.min(end + 1, start + length) - start,
                    start + 2,
                    end - start - 1));
          } else if (subExpression.contains(openBracket)) {
            strElems.add(
                new NestedVarElem(
                    chars,
                    start,
                    Math.min(end + 1, start + length) - start,
                    start + 2,
                    Math.min(end - 2, start + length) - start));
          } else {
            strElems.add(
                new VarElem(
                    chars,
                    start,
                    Math.min(end + 1, start + length) - start,
                    start + 2,
                    Math.min(end - 2, start + length) - start));
          }
        }
      }
      // reset the current index to after the expression, and the start to the beginning of the next
      // expression
      currentInd = end + 1;
      if (currentInd > origLen + offset) {
        currentInd = origLen + offset;
      }
      start = expression.indexOf(openBracket, currentInd);
    }
    // append the rest of the original string, ie after the last expression
    if (currentInd < origLen + offset) {
      strElems.add(new ConstOffsetElem(chars, currentInd, offset + length - currentInd));
    }
    return strElems.toArray(new FlexibleStringExpander[strElems.size()]);
  }

  // Note: a character array is used instead of a String to keep the memory footprint small.
  protected final char[] chars;
  protected int hint = 20;

  protected FlexibleStringExpander(char[] chars) {
    this.chars = chars;
  }

  protected abstract Object get(
      Map<String, ? extends Object> context, TimeZone timeZone, Locale locale);

  private static Locale getLocale(Locale locale, Map<String, ? extends Object> context) {
    if (locale == null) {
      locale = (Locale) context.get("locale");
      if (locale == null && context.containsKey("autoUserLogin")) {
        Map<String, Object> autoUserLogin = UtilGenerics.cast(context.get("autoUserLogin"));
        locale = UtilMisc.ensureLocale(autoUserLogin.get("lastLocale"));
      }
      if (locale == null) {
        locale = Locale.getDefault();
      }
    }
    return locale;
  }

  private static TimeZone getTimeZone(TimeZone timeZone, Map<String, ? extends Object> context) {
    if (timeZone == null) {
      timeZone = (TimeZone) context.get("timeZone");
      if (timeZone == null && context.containsKey("autoUserLogin")) {
        Map<String, String> autoUserLogin = UtilGenerics.cast(context.get("autoUserLogin"));
        timeZone = UtilDateTime.toTimeZone(autoUserLogin.get("lastTimeZone"));
      }
      if (timeZone == null) {
        timeZone = TimeZone.getDefault();
      }
    }
    return timeZone;
  }

  /**
   * Evaluate this object's expression and return the result as a <code>String</code>. Null or empty
   * expressions return an empty <code>String</code>. A <code>null context</code> argument will
   * return the original expression.
   *
   * @param context The evaluation context
   * @return This object's expression result as a <code>String</code>
   */
  public String expandString(Map<String, ? extends Object> context) {
    return this.expandString(context, null, null);
  }

  /**
   * Evaluate this object's expression and return the result as a <code>String</code>. Null or empty
   * expressions return an empty <code>String</code>. A <code>null context</code> argument will
   * return the original expression.
   *
   * @param context The evaluation context
   * @param locale The locale to be used for localization
   * @return This object's expression result as a <code>String</code>
   */
  public String expandString(Map<String, ? extends Object> context, Locale locale) {
    return this.expandString(context, null, locale);
  }

  /**
   * Evaluate this object's expression and return the result as a <code>String</code>. Null or empty
   * expressions return an empty <code>String</code>. A <code>null context</code> argument will
   * return the original expression.
   *
   * @param context The evaluation context
   * @param timeZone The time zone to be used for localization
   * @param locale The locale to be used for localization
   * @return This object's expression result as a <code>String</code>
   */
  public String expandString(
      Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) {
    if (context == null) {
      return this.toString();
    }
    timeZone = getTimeZone(timeZone, context);
    locale = getLocale(locale, context);
    Object obj = get(context, timeZone, locale);
    StringBuilder buffer = new StringBuilder(this.hint);
    try {
      if (obj instanceof String && UtilValidate.isEmpty(obj)) {
      } else if (obj != null) {
        buffer.append(ObjectType.simpleTypeConvert(obj, "String", null, timeZone, locale, true));
      }
    } catch (Exception e) {
      buffer.append(obj);
    }
    if (buffer.length() > this.hint) {
      this.hint = buffer.length();
    }
    return buffer.toString();
  }

  /**
   * Evaluate this object's expression and return the result as an <code>Object</code>. Null or
   * empty expressions return an empty <code>String</code>. A <code>null context</code> argument
   * will return the original expression.
   *
   * @param context The evaluation context
   * @return This object's expression result as a <code>String</code>
   */
  public Object expand(Map<String, ? extends Object> context) {
    return this.expand(context, null, null);
  }

  /**
   * Evaluate this object's expression and return the result as an <code>Object</code>. Null or
   * empty expressions return an empty <code>String</code>. A <code>null context</code> argument
   * will return the original expression.
   *
   * @param context The evaluation context
   * @param locale The locale to be used for localization
   * @return This object's expression result as a <code>String</code>
   */
  public Object expand(Map<String, ? extends Object> context, Locale locale) {
    return this.expand(context, null, locale);
  }

  /**
   * Evaluate this object's expression and return the result as an <code>Object</code>. Null or
   * empty expressions return an empty <code>String</code>. A <code>null context</code> argument
   * will return the original expression.
   *
   * @param context The evaluation context
   * @param timeZone The time zone to be used for localization
   * @param locale The locale to be used for localization
   * @return This object's expression result as a <code>String</code>
   */
  public Object expand(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) {
    if (context == null) {
      return null;
    }
    return get(context, getTimeZone(timeZone, context), getLocale(locale, context));
  }

  /**
   * Returns a copy of the original expression.
   *
   * @return The original expression
   */
  public abstract String getOriginal();

  /**
   * Returns <code>true</code> if the original expression is empty or <code>null</code>.
   *
   * @return <code>true</code> if the original expression is empty or <code>null</code>
   */
  public abstract boolean isEmpty();

  /**
   * Returns a copy of the original expression.
   *
   * @return The original expression
   */
  @Override
  public String toString() {
    return this.getOriginal();
  }

  protected abstract static class ArrayOffsetString extends FlexibleStringExpander {
    protected final int offset;
    protected final int length;

    protected ArrayOffsetString(char[] chars, int offset, int length) {
      super(chars);
      this.offset = offset;
      this.length = length;
    }

    @Override
    public boolean isEmpty() {
      // This is always false; the complex child classes can't be
      // empty, as they contain at least ${; constant elements
      // with a length of 0 will never be created.
      return false;
    }

    @Override
    public String getOriginal() {
      return new String(this.chars, this.offset, this.length);
    }
  }

  /** An object that represents a <code>${bsh:}</code> expression. */
  protected static class BshElem extends ArrayOffsetString {
    private final int parseStart;
    private final int parseLength;

    protected BshElem(char[] chars, int offset, int length, int parseStart, int parseLength) {
      super(chars, offset, length);
      this.parseStart = parseStart;
      this.parseLength = parseLength;
    }

    @Override
    protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) {
      try {
        Object obj =
            BshUtil.eval(
                new String(this.chars, this.parseStart, this.parseLength),
                UtilMisc.makeMapWritable(context));
        if (obj != null) {
          return obj;
        } else {
          if (Debug.verboseOn()) {
            Debug.logVerbose(
                "BSH scriptlet evaluated to null ["
                    + this
                    + "], got no return so inserting nothing.",
                module);
          }
        }
      } catch (EvalError e) {
        Debug.logWarning(
            e,
            "Error evaluating BSH scriptlet [" + this + "], inserting nothing; error was: " + e,
            module);
      }
      return null;
    }
  }

  /** An object that represents a <code>String</code> constant portion of an expression. */
  protected static class ConstSimpleElem extends FlexibleStringExpander {
    protected ConstSimpleElem(char[] chars) {
      super(chars);
    }

    @Override
    public boolean isEmpty() {
      return this.chars.length == 0;
    }

    @Override
    public String getOriginal() {
      return new String(this.chars);
    }

    @Override
    public String expandString(
        Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) {
      return getOriginal();
    }

    @Override
    protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) {
      return isEmpty() ? null : getOriginal();
    }
  }

  /** An object that represents a <code>String</code> constant portion of an expression. */
  protected static class ConstOffsetElem extends ArrayOffsetString {
    protected ConstOffsetElem(char[] chars, int offset, int length) {
      super(chars, offset, length);
    }

    @Override
    protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) {
      return getOriginal();
    }

    @Override
    public String expandString(
        Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) {
      return new String(this.chars, this.offset, this.length);
    }
  }

  /** An object that represents a currency portion of an expression. */
  protected static class CurrElem extends ArrayOffsetString {
    protected final char[] valueStr;
    protected final FlexibleStringExpander codeExpr;

    protected CurrElem(char[] chars, int offset, int length, int parseStart, int parseLength) {
      super(chars, offset, length);
      String parse = new String(chars, parseStart, parseLength);
      int currencyPos = parse.indexOf("?currency(");
      int closeParen = parse.indexOf(")", currencyPos + 10);
      this.codeExpr =
          FlexibleStringExpander.getInstance(
              parse, chars, parseStart + currencyPos + 10, closeParen - currencyPos - 10, true);
      this.valueStr =
          openBracket.concat(parse.substring(0, currencyPos)).concat(closeBracket).toCharArray();
    }

    @Override
    protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) {
      try {
        Object obj = UelUtil.evaluate(context, new String(this.valueStr));
        if (obj != null) {
          String currencyCode = this.codeExpr.expandString(context, timeZone, locale);
          return UtilFormatOut.formatCurrency(new BigDecimal(obj.toString()), currencyCode, locale);
        }
      } catch (PropertyNotFoundException e) {
        if (Debug.verboseOn()) {
          Debug.logVerbose("Error evaluating expression: " + e, module);
        }
      } catch (Exception e) {
        Debug.logError("Error evaluating expression: " + e, module);
      }
      return null;
    }
  }

  /** A container object that contains expression fragments. */
  protected static class Elements extends ArrayOffsetString {
    protected final FlexibleStringExpander[] childElems;

    protected Elements(char[] chars, int offset, int length, FlexibleStringExpander[] childElems) {
      super(chars, offset, length);
      this.childElems = childElems;
    }

    @Override
    protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) {
      StringBuilder buffer = new StringBuilder();
      for (FlexibleStringExpander child : this.childElems) {
        buffer.append(child.expandString(context, timeZone, locale));
      }
      return buffer.toString();
    }
  }

  /** An object that represents a <code>${groovy:}</code> expression. */
  protected static class GroovyElem extends ArrayOffsetString {
    protected final Class<?> parsedScript;

    protected GroovyElem(char[] chars, int offset, int length, int parseStart, int parseLength) {
      super(chars, offset, length);
      this.parsedScript = GroovyUtil.parseClass(new String(chars, parseStart, parseLength));
    }

    @Override
    protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) {
      try {
        Object obj =
            InvokerHelper.createScript(this.parsedScript, GroovyUtil.getBinding(context)).run();
        if (obj != null) {
          return obj;
        } else {
          if (Debug.verboseOn()) {
            Debug.logVerbose(
                "Groovy scriptlet evaluated to null ["
                    + this
                    + "], got no return so inserting nothing.",
                module);
          }
        }
      } catch (Exception e) {
        // handle other things, like the groovy.lang.MissingPropertyException
        Debug.logWarning(
            e,
            "Error evaluating Groovy scriptlet [" + this + "], inserting nothing; error was: " + e,
            module);
      }
      return null;
    }
  }

  /** An object that represents a nested expression. */
  protected static class NestedVarElem extends ArrayOffsetString {
    protected final FlexibleStringExpander[] childElems;

    protected NestedVarElem(char[] chars, int offset, int length, int parseStart, int parseLength) {
      super(chars, offset, length);
      this.childElems = getStrElems(chars, parseStart, parseLength);
      if (length > this.hint) {
        this.hint = length;
      }
    }

    @Override
    protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) {
      StringBuilder expr = new StringBuilder(this.hint);
      for (FlexibleStringExpander child : this.childElems) {
        expr.append(child.expandString(context, timeZone, locale));
      }
      if (expr.length() == 0) {
        return "";
      }
      try {
        return UelUtil.evaluate(context, openBracket.concat(expr.toString()).concat(closeBracket));
      } catch (PropertyNotFoundException e) {
        if (Debug.verboseOn()) {
          Debug.logVerbose("Error evaluating expression: " + e, module);
        }
      } catch (Exception e) {
        Debug.logError("Error evaluating expression: " + e, module);
      }
      return "";
    }
  }

  /** An object that represents a simple, non-nested expression. */
  protected static class VarElem extends ArrayOffsetString {
    protected final char[] bracketedOriginal;

    protected VarElem(char[] chars, int offset, int length, int parseStart, int parseLength) {
      super(chars, offset, length);
      this.bracketedOriginal =
          openBracket
              .concat(UelUtil.prepareExpression(new String(chars, parseStart, parseLength)))
              .concat(closeBracket)
              .toCharArray();
    }

    @Override
    protected Object get(Map<String, ? extends Object> context, TimeZone timeZone, Locale locale) {
      Object obj = null;
      try {
        obj = UelUtil.evaluate(context, new String(this.bracketedOriginal));
      } catch (PropertyNotFoundException e) {
        if (Debug.verboseOn()) {
          Debug.logVerbose("Error evaluating expression " + this + ": " + e, module);
        }
      } catch (Exception e) {
        Debug.logError("Error evaluating expression " + this + ": " + e, module);
      }
      if (obj != null) {
        try {
          // Check for runtime nesting
          String str = (String) obj;
          if (str.contains(openBracket)) {
            FlexibleStringExpander fse = FlexibleStringExpander.getInstance(str);
            return fse.get(context, timeZone, locale);
          }
        } catch (ClassCastException e) {
        }
      }
      return obj;
    }
  }
}
  public void render(
      String name,
      String page,
      String info,
      String contentType,
      String encoding,
      HttpServletRequest request,
      HttpServletResponse response)
      throws ViewHandlerException {
    // some containers call filters on EVERY request, even forwarded ones,
    // so let it know that it came from the control servlet

    if (request == null) {
      throw new ViewHandlerException(
          "The HttpServletRequest object was null, how did that happen?");
    }
    if (UtilValidate.isEmpty(page)) {
      throw new ViewHandlerException("View page was null or empty, but must be specified");
    }
    if (UtilValidate.isEmpty(info)) {
      Debug.logInfo(
          "View info string was null or empty, (optionally used to specify an Entity that is mapped to the Entity Engine datasource that the report will use).",
          module);
    }

    // tell the ContextFilter we are forwarding
    request.setAttribute(ContextFilter.FORWARDED_FROM_SERVLET, Boolean.valueOf(true));
    Delegator delegator = (Delegator) request.getAttribute("delegator");
    if (delegator == null) {
      throw new ViewHandlerException("The delegator object was null, how did that happen?");
    }

    try {
      JasperReport report = (JasperReport) jasperReportsCompiledCache.get(page);
      if (report == null) {
        synchronized (this) {
          report = (JasperReport) jasperReportsCompiledCache.get(page);
          if (report == null) {
            InputStream is = context.getResourceAsStream(page);
            report = JasperCompileManager.compileReport(is);
            jasperReportsCompiledCache.put(page, report);
          }
        }
      }

      response.setContentType("application/xls");

      Map parameters = (Map) request.getAttribute("jrParameters");
      if (parameters == null) {
        parameters = UtilHttp.getParameterMap(request);
      }

      JRDataSource jrDataSource = (JRDataSource) request.getAttribute("jrDataSource");
      JasperPrint jp = null;
      if (jrDataSource == null) {
        String datasourceName = delegator.getEntityHelperName(info);
        if (UtilValidate.isNotEmpty(datasourceName)) {
          Debug.logInfo(
              "Filling report with connection from datasource: " + datasourceName, module);
          jp =
              JasperFillManager.fillReport(
                  report, parameters, ConnectionFactory.getConnection(datasourceName));
        } else {
          Debug.logInfo("Filling report with an empty JR datasource", module);
          jp = JasperFillManager.fillReport(report, parameters, new JREmptyDataSource());
        }
      } else {
        Debug.logInfo("Filling report with a passed in jrDataSource", module);
        jp = JasperFillManager.fillReport(report, parameters, jrDataSource);
      }

      if (jp.getPages().size() < 1) {
        throw new ViewHandlerException("Report is Empty (no results?)");
      } else {
        Debug.logInfo("Got report, there are " + jp.getPages().size() + " pages.", module);
      }
      JExcelApiExporter exporter = new JExcelApiExporter();
      exporter.setParameter(JExcelApiExporterParameter.JASPER_PRINT, jp);
      exporter.setParameter(JExcelApiExporterParameter.OUTPUT_STREAM, response.getOutputStream());
      exporter.exportReport();

    } catch (IOException ie) {
      throw new ViewHandlerException("IO Error in report", ie);
    } catch (java.sql.SQLException e) {
      throw new ViewHandlerException("Database error while running report", e);
    } catch (Exception e) {
      throw new ViewHandlerException("Error in report", e);
      // } catch (ServletException se) {
      // throw new ViewHandlerException("Error in region", se.getRootCause());
    }
  }
Beispiel #9
0
/** Widget Library - Tree factory class */
public class TreeFactory {

  public static final String module = TreeFactory.class.getName();

  public static final UtilCache<String, Map<String, ModelTree>> treeLocationCache =
      UtilCache.createUtilCache("widget.tree.locationResource", 0, 0, false);
  public static final UtilCache<String, Map<String, ModelTree>> treeWebappCache =
      UtilCache.createUtilCache("widget.tree.webappResource", 0, 0, false);

  public static ModelTree getTreeFromLocation(
      String resourceName, String treeName, Delegator delegator, LocalDispatcher dispatcher)
      throws IOException, SAXException, ParserConfigurationException {
    Map<String, ModelTree> modelTreeMap = treeLocationCache.get(resourceName);
    if (modelTreeMap == null) {
      synchronized (TreeFactory.class) {
        modelTreeMap = treeLocationCache.get(resourceName);
        if (modelTreeMap == null) {
          ClassLoader loader = Thread.currentThread().getContextClassLoader();
          if (loader == null) {
            loader = TreeFactory.class.getClassLoader();
          }

          URL treeFileUrl = null;
          treeFileUrl = FlexibleLocation.resolveLocation(resourceName); // , loader);
          Document treeFileDoc = UtilXml.readXmlDocument(treeFileUrl, true);
          modelTreeMap = readTreeDocument(treeFileDoc, delegator, dispatcher, resourceName);
          treeLocationCache.put(resourceName, modelTreeMap);
        }
      }
    }

    ModelTree modelTree = (ModelTree) modelTreeMap.get(treeName);
    if (modelTree == null) {
      throw new IllegalArgumentException(
          "Could not find tree with name ["
              + treeName
              + "] in class resource ["
              + resourceName
              + "]");
    }
    return modelTree;
  }

  public static ModelTree getTreeFromWebappContext(
      String resourceName, String treeName, HttpServletRequest request)
      throws IOException, SAXException, ParserConfigurationException {
    String webappName = UtilHttp.getApplicationName(request);
    String cacheKey = webappName + "::" + resourceName;

    Map<String, ModelTree> modelTreeMap = treeWebappCache.get(cacheKey);
    if (modelTreeMap == null) {
      synchronized (TreeFactory.class) {
        modelTreeMap = treeWebappCache.get(cacheKey);
        if (modelTreeMap == null) {
          ServletContext servletContext = (ServletContext) request.getAttribute("servletContext");
          Delegator delegator = (Delegator) request.getAttribute("delegator");
          LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");

          URL treeFileUrl = servletContext.getResource(resourceName);
          Document treeFileDoc = UtilXml.readXmlDocument(treeFileUrl, true);
          modelTreeMap = readTreeDocument(treeFileDoc, delegator, dispatcher, cacheKey);
          treeWebappCache.put(cacheKey, modelTreeMap);
        }
      }
    }

    ModelTree modelTree = (ModelTree) modelTreeMap.get(treeName);
    if (modelTree == null) {
      throw new IllegalArgumentException(
          "Could not find tree with name ["
              + treeName
              + "] in webapp resource ["
              + resourceName
              + "] in the webapp ["
              + webappName
              + "]");
    }
    return modelTree;
  }

  public static Map<String, ModelTree> readTreeDocument(
      Document treeFileDoc, Delegator delegator, LocalDispatcher dispatcher, String treeLocation) {
    Map<String, ModelTree> modelTreeMap = new HashMap<String, ModelTree>();
    if (treeFileDoc != null) {
      // read document and construct ModelTree for each tree element
      Element rootElement = treeFileDoc.getDocumentElement();
      for (Element treeElement : UtilXml.childElementList(rootElement, "tree")) {
        ModelTree modelTree = new ModelTree(treeElement, delegator, dispatcher);
        modelTree.setTreeLocation(treeLocation);
        modelTreeMap.put(modelTree.getName(), modelTree);
      }
    }
    return modelTreeMap;
  }
}
Beispiel #10
0
/** EntityEcaUtil */
public class EntityEcaUtil {

  public static final String module = EntityEcaUtil.class.getName();

  public static UtilCache<String, Map<String, Map<String, List<EntityEcaRule>>>> entityEcaReaders =
      UtilCache.createUtilCache("entity.EcaReaders", 0, 0, false);

  public static Map<String, Map<String, List<EntityEcaRule>>> getEntityEcaCache(
      String entityEcaReaderName) {
    Map<String, Map<String, List<EntityEcaRule>>> ecaCache =
        entityEcaReaders.get(entityEcaReaderName);
    if (ecaCache == null) {
      synchronized (EntityEcaUtil.class) {
        ecaCache = entityEcaReaders.get(entityEcaReaderName);
        if (ecaCache == null) {
          ecaCache = FastMap.newInstance();
          readConfig(entityEcaReaderName, ecaCache);
          entityEcaReaders.put(entityEcaReaderName, ecaCache);
        }
      }
    }
    return ecaCache;
  }

  public static String getEntityEcaReaderName(String delegatorName) {
    DelegatorInfo delegatorInfo = EntityConfigUtil.getDelegatorInfo(delegatorName);
    if (delegatorInfo == null) {
      Debug.logError(
          "BAD ERROR: Could not find delegator config with name: " + delegatorName, module);
      return null;
    }
    return delegatorInfo.entityEcaReader;
  }

  protected static void readConfig(
      String entityEcaReaderName, Map<String, Map<String, List<EntityEcaRule>>> ecaCache) {
    EntityEcaReaderInfo entityEcaReaderInfo =
        EntityConfigUtil.getEntityEcaReaderInfo(entityEcaReaderName);
    if (entityEcaReaderInfo == null) {
      Debug.logError(
          "BAD ERROR: Could not find entity-eca-reader config with name: " + entityEcaReaderName,
          module);
      return;
    }

    for (Element eecaResourceElement : entityEcaReaderInfo.resourceElements) {
      ResourceHandler handler =
          new MainResourceHandler(EntityConfigUtil.ENTITY_ENGINE_XML_FILENAME, eecaResourceElement);
      addEcaDefinitions(handler, ecaCache);
    }

    // get all of the component resource eca stuff, ie specified in each ofbiz-component.xml file
    for (ComponentConfig.EntityResourceInfo componentResourceInfo :
        ComponentConfig.getAllEntityResourceInfos("eca")) {
      if (entityEcaReaderName.equals(componentResourceInfo.readerName)) {
        addEcaDefinitions(componentResourceInfo.createResourceHandler(), ecaCache);
      }
    }
  }

  protected static void addEcaDefinitions(
      ResourceHandler handler, Map<String, Map<String, List<EntityEcaRule>>> ecaCache) {
    Element rootElement = null;
    try {
      rootElement = handler.getDocument().getDocumentElement();
    } catch (GenericConfigException e) {
      Debug.logError(e, module);
      return;
    }

    int numDefs = 0;
    for (Element e : UtilXml.childElementList(rootElement, "eca")) {
      String entityName = e.getAttribute("entity");
      String eventName = e.getAttribute("event");
      Map<String, List<EntityEcaRule>> eventMap = ecaCache.get(entityName);
      List<EntityEcaRule> rules = null;
      if (eventMap == null) {
        eventMap = FastMap.newInstance();
        rules = FastList.newInstance();
        ecaCache.put(entityName, eventMap);
        eventMap.put(eventName, rules);
      } else {
        rules = eventMap.get(eventName);
        if (rules == null) {
          rules = FastList.newInstance();
          eventMap.put(eventName, rules);
        }
      }
      rules.add(new EntityEcaRule(e));
      numDefs++;
    }
    try {
      Debug.logImportant(
          "Loaded ["
              + numDefs
              + "] Entity ECA definitions from "
              + handler.getFullLocation()
              + " in loader "
              + handler.getLoaderName(),
          module);
    } catch (GenericConfigException e) {
      Debug.logError(e, module);
      return;
    }
  }

  public static Collection<EntityEcaRule> getEntityEcaRules(
      Delegator delegator, String entityName, String event) {
    Map<String, Map<String, List<EntityEcaRule>>> ecaCache =
        EntityEcaUtil.getEntityEcaCache(
            EntityEcaUtil.getEntityEcaReaderName(delegator.getDelegatorName()));
    Map<String, List<EntityEcaRule>> eventMap = ecaCache.get(entityName);
    if (eventMap != null) {
      if (event != null) {
        return eventMap.get(event);
      }
    }
    return null;
  }
}
Beispiel #11
0
/** Common Services */
public class CommonEvents {

  public static final String module = CommonEvents.class.getName();

  public static UtilCache<String, Map<String, String>> appletSessions =
      UtilCache.createUtilCache("AppletSessions", 0, 600000, true);

  public static String checkAppletRequest(
      HttpServletRequest request, HttpServletResponse response) {
    Delegator delegator = (Delegator) request.getAttribute("delegator");
    String sessionId = request.getParameter("sessionId");
    String visitId = request.getParameter("visitId");
    sessionId = sessionId.trim();
    visitId = visitId.trim();

    String responseString = "";

    GenericValue visit = null;
    try {
      visit = delegator.findOne("Visit", false, "visitId", visitId);
    } catch (GenericEntityException e) {
      Debug.logError(e, "Cannot Visit Object", module);
    }

    if (visit != null
        && visit.getString("sessionId").equals(sessionId)
        && appletSessions.containsKey(sessionId)) {
      Map<String, String> sessionMap = appletSessions.get(sessionId);
      if (sessionMap != null && sessionMap.containsKey("followPage"))
        responseString = sessionMap.remove("followPage");
    }

    try {
      PrintWriter out = response.getWriter();
      response.setContentType("text/plain");
      out.println(responseString);
      out.close();
    } catch (IOException e) {
      Debug.logError(e, "Problems writing servlet output!", module);
    }

    return "success";
  }

  public static String receiveAppletRequest(
      HttpServletRequest request, HttpServletResponse response) {
    Delegator delegator = (Delegator) request.getAttribute("delegator");
    String sessionId = request.getParameter("sessionId");
    String visitId = request.getParameter("visitId");
    sessionId = sessionId.trim();
    visitId = visitId.trim();

    String responseString = "ERROR";

    GenericValue visit = null;
    try {
      visit = delegator.findOne("Visit", false, "visitId", visitId);
    } catch (GenericEntityException e) {
      Debug.logError(e, "Cannot Visit Object", module);
    }

    if (visit.getString("sessionId").equals(sessionId)) {
      String currentPage = request.getParameter("currentPage");
      if (appletSessions.containsKey(sessionId)) {
        Map<String, String> sessionMap = appletSessions.get(sessionId);
        String followers = sessionMap.get("followers");
        List<String> folList = StringUtil.split(followers, ",");
        for (String follower : folList) {
          Map<String, String> folSesMap = UtilMisc.toMap("followPage", currentPage);
          appletSessions.put(follower, folSesMap);
        }
      }
      responseString = "OK";
    }

    try {
      PrintWriter out = response.getWriter();
      response.setContentType("text/plain");
      out.println(responseString);
      out.close();
    } catch (IOException e) {
      Debug.logError(e, "Problems writing servlet output!", module);
    }

    return "success";
  }

  public static String setAppletFollower(HttpServletRequest request, HttpServletResponse response) {
    Security security = (Security) request.getAttribute("security");
    GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin");
    String visitId = request.getParameter("visitId");
    if (visitId != null) request.setAttribute("visitId", visitId);
    if (security.hasPermission("SEND_CONTROL_APPLET", userLogin)) {
      String followerSessionId = request.getParameter("followerSid");
      String followSessionId = request.getParameter("followSid");
      Map<String, String> follow = appletSessions.get(followSessionId);
      if (follow == null) follow = FastMap.newInstance();
      String followerListStr = follow.get("followers");
      if (followerListStr == null) {
        followerListStr = followerSessionId;
      } else {
        followerListStr = followerListStr + "," + followerSessionId;
      }
      appletSessions.put(followSessionId, follow);
      appletSessions.put(followerSessionId, null);
    }
    return "success";
  }

  public static String setFollowerPage(HttpServletRequest request, HttpServletResponse response) {
    Security security = (Security) request.getAttribute("security");
    GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin");
    String visitId = request.getParameter("visitId");
    if (visitId != null) request.setAttribute("visitId", visitId);
    if (security.hasPermission("SEND_CONTROL_APPLET", userLogin)) {
      String followerSessionId = request.getParameter("followerSid");
      String pageUrl = request.getParameter("pageUrl");
      Map<String, String> follow = appletSessions.get(followerSessionId);
      if (follow == null) follow = FastMap.newInstance();
      follow.put("followPage", pageUrl);
      appletSessions.put(followerSessionId, follow);
    }
    return "success";
  }

  /**
   * Simple event to set the users per-session locale setting. The user's locale setting should be
   * passed as a "newLocale" request parameter.
   */
  public static String setSessionLocale(HttpServletRequest request, HttpServletResponse response) {
    String localeString = request.getParameter("newLocale");
    if (UtilValidate.isNotEmpty(localeString)) {
      UtilHttp.setLocale(request, localeString);

      // update the UserLogin object
      GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin");
      if (userLogin == null) {
        userLogin = (GenericValue) request.getSession().getAttribute("autoUserLogin");
      }

      if (userLogin != null) {
        GenericValue ulUpdate = GenericValue.create(userLogin);
        ulUpdate.set("lastLocale", localeString);
        try {
          ulUpdate.store();
          userLogin.refreshFromCache();
        } catch (GenericEntityException e) {
          Debug.logWarning(e, module);
        }
      }
    }
    return "success";
  }

  /** Simple event to set the user's per-session time zone setting. */
  public static String setSessionTimeZone(
      HttpServletRequest request, HttpServletResponse response) {
    String tzString = request.getParameter("tzId");
    if (UtilValidate.isNotEmpty(tzString)) {
      UtilHttp.setTimeZone(request, tzString);

      // update the UserLogin object
      GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin");
      if (userLogin == null) {
        userLogin = (GenericValue) request.getSession().getAttribute("autoUserLogin");
      }

      if (userLogin != null) {
        GenericValue ulUpdate = GenericValue.create(userLogin);
        ulUpdate.set("lastTimeZone", tzString);
        try {
          ulUpdate.store();
          userLogin.refreshFromCache();
        } catch (GenericEntityException e) {
          Debug.logWarning(e, module);
        }
      }
    }
    return "success";
  }

  /** Simple event to set the users per-session currency uom value */
  public static String setSessionCurrencyUom(
      HttpServletRequest request, HttpServletResponse response) {
    String currencyUom = request.getParameter("currencyUom");
    if (UtilValidate.isNotEmpty(currencyUom)) {
      // update the session
      UtilHttp.setCurrencyUom(request.getSession(), currencyUom);

      // update the UserLogin object
      GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin");
      if (userLogin == null) {
        userLogin = (GenericValue) request.getSession().getAttribute("autoUserLogin");
      }

      if (userLogin != null) {
        GenericValue ulUpdate = GenericValue.create(userLogin);
        ulUpdate.set("lastCurrencyUom", currencyUom);
        try {
          ulUpdate.store();
          userLogin.refreshFromCache();
        } catch (GenericEntityException e) {
          Debug.logWarning(e, module);
        }
      }
    }
    return "success";
  }

  public static String jsonResponseFromRequestAttributes(
      HttpServletRequest request, HttpServletResponse response) {
    // pull out the service response from the request attribute
    Map<String, Object> attrMap = UtilHttp.getJSONAttributeMap(request);

    // create a JSON Object for return
    JSONObject json = JSONObject.fromObject(attrMap);
    writeJSONtoResponse(json, response);

    return "success";
  }

  private static void writeJSONtoResponse(JSON json, HttpServletResponse response) {
    String jsonStr = json.toString();
    if (jsonStr == null) {
      Debug.logError("JSON Object was empty; fatal error!", module);
      return;
    }

    // set the X-JSON content type
    response.setContentType("application/x-json");
    // jsonStr.length is not reliable for unicode characters
    try {
      response.setContentLength(jsonStr.getBytes("UTF8").length);
    } catch (UnsupportedEncodingException e) {
      Debug.logError("Problems with Json encoding: " + e, module);
    }

    // return the JSON String
    Writer out;
    try {
      out = response.getWriter();
      out.write(jsonStr);
      out.flush();
    } catch (IOException e) {
      Debug.logError(e, module);
    }
  }

  public static String getJSONuiLabelArray(
      HttpServletRequest request, HttpServletResponse response) {
    String requiredLabels = request.getParameter("requiredLabels");

    JSONObject uiLabelObject = null;
    if (UtilValidate.isNotEmpty(requiredLabels)) {
      // Transform JSON String to Object
      uiLabelObject = (JSONObject) JSONSerializer.toJSON(requiredLabels);
    }

    JSONObject jsonUiLabel = new JSONObject();
    Locale locale = request.getLocale();
    if (!uiLabelObject.isEmpty()) {
      Set<String> resourceSet = UtilGenerics.checkSet(uiLabelObject.keySet());
      // Iterate over the resouce set
      for (String resource : resourceSet) {
        JSONArray labels = uiLabelObject.getJSONArray(resource);
        if (labels.isEmpty() || labels == null) {
          continue;
        }

        // Iterate over the uiLabel List
        Iterator<String> jsonLabelIterator = UtilGenerics.cast(labels.iterator());
        JSONArray resourceLabelList = new JSONArray();
        while (jsonLabelIterator.hasNext()) {
          String label = jsonLabelIterator.next();
          String receivedLabel = UtilProperties.getMessage(resource, label, locale);
          if (UtilValidate.isNotEmpty(receivedLabel)) {
            resourceLabelList.add(receivedLabel);
          }
        }
        jsonUiLabel.element(resource, resourceLabelList);
      }
    }

    writeJSONtoResponse(jsonUiLabel, response);
    return "success";
  }

  public static String getJSONuiLabel(HttpServletRequest request, HttpServletResponse response) {
    String requiredLabels = request.getParameter("requiredLabel");

    JSONObject uiLabelObject = null;
    if (UtilValidate.isNotEmpty(requiredLabels)) {
      // Transform JSON String to Object
      uiLabelObject = (JSONObject) JSONSerializer.toJSON(requiredLabels);
    }

    JSONArray jsonUiLabel = new JSONArray();
    Locale locale = request.getLocale();
    if (!uiLabelObject.isEmpty()) {
      Set<String> resourceSet = UtilGenerics.checkSet(uiLabelObject.keySet());
      // Iterate over the resource set
      // here we need a keySet because we don't now which label resource to load
      // the key set should have the size one, if greater or empty error should returned
      if (UtilValidate.isEmpty(resourceSet)) {
        Debug.logError("No resource and labels found", module);
        return "error";
      } else if (resourceSet.size() > 1) {
        Debug.logError(
            "More than one resource found, please use the method: getJSONuiLabelArray", module);
        return "error";
      }

      for (String resource : resourceSet) {
        String label = uiLabelObject.getString(resource);
        if (UtilValidate.isEmail(label)) {
          continue;
        }

        String receivedLabel = UtilProperties.getMessage(resource, label, locale);
        jsonUiLabel.add(receivedLabel);
      }
    }

    writeJSONtoResponse(jsonUiLabel, response);
    return "success";
  }

  public static String getCaptcha(HttpServletRequest request, HttpServletResponse response) {
    try {
      final String captchaSizeConfigName =
          StringUtils.defaultIfEmpty(request.getParameter("captchaSize"), "default");
      final String captchaSizeConfig =
          UtilProperties.getPropertyValue("captcha.properties", "captcha." + captchaSizeConfigName);
      final String[] captchaSizeConfigs = captchaSizeConfig.split("\\|");
      final String captchaCodeId =
          StringUtils.defaultIfEmpty(
              request.getParameter("captchaCodeId"),
              ""); // this is used to uniquely identify in the user session the attribute where the
                   // captcha code for the last captcha for the form is stored

      final int fontSize = Integer.parseInt(captchaSizeConfigs[0]);
      final int height = Integer.parseInt(captchaSizeConfigs[1]);
      final int width = Integer.parseInt(captchaSizeConfigs[2]);
      final int charsToPrint =
          UtilProperties.getPropertyAsInteger("captcha.properties", "captcha.code_length", 6);
      final char[] availableChars =
          UtilProperties.getPropertyValue("captcha.properties", "captcha.characters").toCharArray();

      // It is possible to pass the font size, image width and height with the request as well
      Color backgroundColor = Color.gray;
      Color borderColor = Color.DARK_GRAY;
      Color textColor = Color.ORANGE;
      Color circleColor = new Color(160, 160, 160);
      Font textFont = new Font("Arial", Font.PLAIN, fontSize);
      int circlesToDraw = 6;
      float horizMargin = 20.0f;
      double rotationRange = 0.7; // in radians
      BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

      Graphics2D g = (Graphics2D) bufferedImage.getGraphics();

      g.setColor(backgroundColor);
      g.fillRect(0, 0, width, height);

      // Generating some circles for background noise
      g.setColor(circleColor);
      for (int i = 0; i < circlesToDraw; i++) {
        int circleRadius = (int) (Math.random() * height / 2.0);
        int circleX = (int) (Math.random() * width - circleRadius);
        int circleY = (int) (Math.random() * height - circleRadius);
        g.drawOval(circleX, circleY, circleRadius * 2, circleRadius * 2);
      }
      g.setColor(textColor);
      g.setFont(textFont);

      FontMetrics fontMetrics = g.getFontMetrics();
      int maxAdvance = fontMetrics.getMaxAdvance();
      int fontHeight = fontMetrics.getHeight();

      String captchaCode = RandomStringUtils.random(6, availableChars);

      float spaceForLetters = -horizMargin * 2 + width;
      float spacePerChar = spaceForLetters / (charsToPrint - 1.0f);

      for (int i = 0; i < captchaCode.length(); i++) {

        // this is a separate canvas used for the character so that
        // we can rotate it independently
        int charWidth = fontMetrics.charWidth(captchaCode.charAt(i));
        int charDim = Math.max(maxAdvance, fontHeight);
        int halfCharDim = (charDim / 2);

        BufferedImage charImage = new BufferedImage(charDim, charDim, BufferedImage.TYPE_INT_ARGB);
        Graphics2D charGraphics = charImage.createGraphics();
        charGraphics.translate(halfCharDim, halfCharDim);
        double angle = (Math.random() - 0.5) * rotationRange;
        charGraphics.transform(AffineTransform.getRotateInstance(angle));
        charGraphics.translate(-halfCharDim, -halfCharDim);
        charGraphics.setColor(textColor);
        charGraphics.setFont(textFont);

        int charX = (int) (0.5 * charDim - 0.5 * charWidth);
        charGraphics.drawString(
            "" + captchaCode.charAt(i),
            charX,
            ((charDim - fontMetrics.getAscent()) / 2 + fontMetrics.getAscent()));

        float x = horizMargin + spacePerChar * (i) - charDim / 2.0f;
        int y = ((height - charDim) / 2);

        g.drawImage(charImage, (int) x, y, charDim, charDim, null, null);

        charGraphics.dispose();
      }
      // Drawing the image border
      g.setColor(borderColor);
      g.drawRect(0, 0, width - 1, height - 1);
      g.dispose();
      response.setContentType("image/jpeg");
      ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
      HttpSession session = request.getSession();
      Map captchaCodeMap = (Map) session.getAttribute("_CAPTCHA_CODE_");
      if (captchaCodeMap == null) {
        captchaCodeMap = new HashMap();
        session.setAttribute("_CAPTCHA_CODE_", captchaCodeMap);
      }
      captchaCodeMap.put(captchaCodeId, captchaCode);
    } catch (Exception ioe) {
      Debug.logError(ioe.getMessage(), module);
    }
    return "success";
  }
}