stefan      2004/05/30 14:40:40

  Modified:    proposals/jcrri/src/org/apache/slide/jcr/core
                        HierarchyManager.java ItemImpl.java
                        ItemManager.java NodeImpl.java PropertyImpl.java
               proposals/jcrri/src/org/apache/slide/jcr/core/state
                        TransientItemStateManager.java
               proposals/jcrri/src/org/apache/slide/jcr/core/state/xml
                        XMLPersistenceManager.java
               proposals/jcrri/src/org/apache/slide/jcr/fs
                        FileSystemPathUtil.java FileSystemResource.java
  Log:
  no message
  
  Revision  Changes    Path
  1.2       +8 -0      
jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/HierarchyManager.java
  
  Index: HierarchyManager.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/HierarchyManager.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- HierarchyManager.java     28 May 2004 18:23:46 -0000      1.1
  +++ HierarchyManager.java     30 May 2004 21:40:40 -0000      1.2
  @@ -82,4 +82,12 @@
        * @throws RepositoryException
        */
       public ItemId[] listChildren(NodeId id) throws ItemNotFoundException, 
RepositoryException;
  +
  +    /**
  +     * @param id
  +     * @return
  +     * @throws ItemNotFoundException
  +     * @throws RepositoryException
  +     */
  +    public ItemId[] listZombieChildren(NodeId id) throws ItemNotFoundException, 
RepositoryException;
   }
  
  
  
  1.2       +11 -2     
jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/ItemImpl.java
  
  Index: ItemImpl.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/ItemImpl.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ItemImpl.java     28 May 2004 18:23:46 -0000      1.1
  +++ ItemImpl.java     30 May 2004 21:40:40 -0000      1.2
  @@ -418,7 +418,7 @@
        // and definitively remove each one
        while (iter.hasNext()) {
            ItemState transientState = (ItemState) iter.next();
  -         PersistentNodeState persistentState = (PersistentNodeState) 
transientState.getOverlayedState();
  +         PersistableItemState persistentState = (PersistableItemState) 
transientState.getOverlayedState();
            // remove persistent state (incl. descendents, if there are any)
   
            // this will indirectly (through stateDestroyed listener method)
  @@ -754,10 +754,19 @@
        checkItemState();
   
        if (keepChanges) {
  -         /** FIXME should reset Item#status field to STATUS_NORMAL
  +         /** todo FIXME should reset Item#status field to STATUS_NORMAL
             * of all descendent non-transient instances; maybe also
             * have to reset stale ItemState instances */
            return;
  +     }
  +
  +     if (isNode()) {
  +         // check if this is the repository root node
  +         if (((NodeImpl) this).isRepositoryRoot()) {
  +             // optimization
  +             itemMgr.getTransientStateMgr().disposeAllItemStates();
  +             return;
  +         }
        }
   
        // list of transient items that should be discarded
  
  
  
  1.2       +36 -0     
jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/ItemManager.java
  
  Index: ItemManager.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/ItemManager.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ItemManager.java  28 May 2004 18:23:46 -0000      1.1
  +++ ItemManager.java  30 May 2004 21:40:40 -0000      1.2
  @@ -609,6 +609,42 @@
       }
   
       /**
  +     * @see HierarchyManager#listZombieChildren(NodeId)
  +     */
  +    public ItemId[] listZombieChildren(NodeId id) throws ItemNotFoundException, 
RepositoryException {
  +     // FIXME messy code
  +     NodeState parentState;
  +     try {
  +         // get transient/persistent state
  +         parentState = (NodeState) getItemState(id);
  +     } catch (NoSuchItemStateException e) {
  +         // try attic
  +         try {
  +             parentState = (NodeState) transientStateMgr.getItemStateInAttic(id);
  +         } catch (NoSuchItemStateException nsise) {
  +             String msg = "failed to retrieve state of parent node " + id;
  +             log.error(msg, nsise);
  +             throw new ItemNotFoundException(msg, nsise);
  +         }
  +     }
  +
  +     ArrayList list = new ArrayList();
  +     Iterator iter = parentState.getRemovedPropertyEntries().iterator();
  +     while (iter.hasNext()) {
  +         // removed properties
  +         NodeState.PropertyEntry pe = (NodeState.PropertyEntry) iter.next();
  +         list.add(new PropertyId(id.getUUID(), pe.getName()));
  +     }
  +     iter = parentState.getRemovedChildNodeEntries().iterator();
  +     while (iter.hasNext()) {
  +         // removed child nodes
  +         NodeState.ChildNodeEntry cne = (NodeState.ChildNodeEntry) iter.next();
  +         list.add(new NodeId(cne.getUUID()));
  +     }
  +     return (ItemId[]) list.toArray(new ItemId[list.size()]);
  +    }
  +
  +    /**
        * @see HierarchyManager#resolvePath(Path)
        */
       public synchronized ItemId resolvePath(Path path)
  
  
  
  1.12      +5 -0      
jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/NodeImpl.java
  
  Index: NodeImpl.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/NodeImpl.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- NodeImpl.java     28 May 2004 18:23:46 -0000      1.11
  +++ NodeImpl.java     30 May 2004 21:40:40 -0000      1.12
  @@ -440,6 +440,11 @@
        return nodeType.isDerivedFrom(ntName);
       }
   
  +    protected boolean isRepositoryRoot() {
  +     RepositoryImpl rep = (RepositoryImpl) ticket.getRepository();
  +     return ((NodeState)state).getUUID().equals(rep.getRootNodeUUID());
  +    }
  +
       //-----------------------------------------------------------------< Item >
       /**
        * @see Item#isNode()
  
  
  
  1.11      +70 -15    
jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/PropertyImpl.java
  
  Index: PropertyImpl.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/PropertyImpl.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- PropertyImpl.java 28 May 2004 18:23:46 -0000      1.10
  +++ PropertyImpl.java 30 May 2004 21:40:40 -0000      1.11
  @@ -258,12 +258,27 @@
       public long getLong() throws ValueFormatException, RepositoryException {
        // check state of this instance
        checkItemState();
  -
  +/*
        Value val = getValue();
        if (val == null) {
            throw new ValueFormatException("null value");
        }
        return val.getLong();
  +*/
  +     // avoid unnecessary object creation if possible
  +     PropertyState state = (PropertyState) getItemState();
  +     InternalValue[] internalValues = state.getValues();
  +     if (internalValues == null || internalValues.length == 0 ||
  +             internalValues[0] == null) {
  +         throw new ValueFormatException("null value");
  +     }
  +     InternalValue val = internalValues[0];
  +     int type = val.getType();
  +     if (type == PropertyType.LONG) {
  +         return ((Long) val.internalValue()).longValue();
  +     }
  +     // not a LONG value, delegate conversion to Value object
  +     return val.toJCRValue(ticket.getNamespaceResolver()).getLong();
       }
   
       /**
  @@ -272,12 +287,27 @@
       public double getDouble() throws ValueFormatException, RepositoryException {
        // check state of this instance
        checkItemState();
  -
  +/*
        Value val = getValue();
        if (val == null) {
            throw new ValueFormatException("null value");
        }
        return val.getDouble();
  +*/
  +     // avoid unnecessary object creation if possible
  +     PropertyState state = (PropertyState) getItemState();
  +     InternalValue[] internalValues = state.getValues();
  +     if (internalValues == null || internalValues.length == 0 ||
  +             internalValues[0] == null) {
  +         throw new ValueFormatException("null value");
  +     }
  +     InternalValue val = internalValues[0];
  +     int type = val.getType();
  +     if (type == PropertyType.DOUBLE) {
  +         return ((Double) val.internalValue()).doubleValue();
  +     }
  +     // not a DOUBLE value, delegate conversion to Value object
  +     return val.toJCRValue(ticket.getNamespaceResolver()).getDouble();
       }
   
       /**
  @@ -286,12 +316,27 @@
       public Calendar getDate() throws ValueFormatException, RepositoryException {
        // check state of this instance
        checkItemState();
  -
  +/*
        Value val = getValue();
        if (val == null) {
            throw new ValueFormatException("null value");
        }
        return val.getDate();
  +*/
  +     // avoid unnecessary object creation if possible
  +     PropertyState state = (PropertyState) getItemState();
  +     InternalValue[] internalValues = state.getValues();
  +     if (internalValues == null || internalValues.length == 0 ||
  +             internalValues[0] == null) {
  +         throw new ValueFormatException("null value");
  +     }
  +     InternalValue val = internalValues[0];
  +     int type = val.getType();
  +     if (type == PropertyType.DATE) {
  +         return (Calendar) val.internalValue();
  +     }
  +     // not a DATE value, delegate conversion to Value object
  +     return val.toJCRValue(ticket.getNamespaceResolver()).getDate();
       }
   
       /**
  @@ -300,12 +345,27 @@
       public boolean getBoolean() throws ValueFormatException, RepositoryException {
        // check state of this instance
        checkItemState();
  -
  +/*
        Value val = getValue();
        if (val == null) {
            throw new ValueFormatException("null value");
        }
        return val.getBoolean();
  +*/
  +     // avoid unnecessary object creation if possible
  +     PropertyState state = (PropertyState) getItemState();
  +     InternalValue[] internalValues = state.getValues();
  +     if (internalValues == null || internalValues.length == 0 ||
  +             internalValues[0] == null) {
  +         throw new ValueFormatException("null value");
  +     }
  +     InternalValue val = internalValues[0];
  +     int type = val.getType();
  +     if (type == PropertyType.BOOLEAN) {
  +         return ((Boolean) val.internalValue()).booleanValue();
  +     }
  +     // not a BOOLEAN value, delegate conversion to Value object
  +     return val.toJCRValue(ticket.getNamespaceResolver()).getBoolean();
       }
   
       /**
  @@ -315,15 +375,14 @@
        // check state of this instance
        checkItemState();
   
  -     InternalValue[] values = ((PropertyState) state).getValues();
  -     if (values == null || values.length == 0) {
  -         throw new ValueFormatException("null value");
  -     }
  -     InternalValue value = values[0];
  -     if (value == null) {
  +     PropertyState state = (PropertyState) getItemState();
  +     InternalValue[] values = state.getValues();
  +     if (values == null || values.length == 0 ||
  +             values[0] == null) {
            throw new ValueFormatException("null value");
        }
   
  +     InternalValue value = values[0];
        if (value.getType() == PropertyType.SOFTLINK) {
            // softlink, i.e. target path
            Path targetPath = (Path) value.internalValue();
  @@ -651,14 +710,10 @@
        //checkItemState();
   
        InternalValue[] values = ((PropertyState) state).getValues();
  -     if (values == null || values.length == 0) {
  +     if (values == null || values.length == 0 || values[0] == null) {
            return 0;
        }
        InternalValue value = values[0];
  -     if (value == null) {
  -         return 0;
  -     }
  -
        switch (value.getType()) {
            case PropertyType.STRING:
            case PropertyType.LONG:
  
  
  
  1.2       +101 -8    
jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/state/TransientItemStateManager.java
  
  Index: TransientItemStateManager.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/state/TransientItemStateManager.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- TransientItemStateManager.java    28 May 2004 18:23:47 -0000      1.1
  +++ TransientItemStateManager.java    30 May 2004 21:40:40 -0000      1.2
  @@ -59,18 +59,18 @@
        attic = new Attic();
       }
   
  -    private void collectDescendantItemStates(ItemId id, ItemStateCache cache, List 
descendents) {
  +    private void collectDescendantItemStates(ItemId id, List descendents) {
        try {
            if (id.denotesNode()) {
                NodeId parentId = (NodeId) id;
                ItemId[] childIds = hierMgr.listChildren(parentId);
                for (int i = 0; i < childIds.length; i++) {
                    ItemId childId = childIds[i];
  -                 if (cache.isCached(childId)) {
  -                     descendents.add(cache.retrieve(childId));
  +                 if (isCached(childId)) {
  +                     descendents.add(retrieve(childId));
                    }
                    // recurse
  -                 collectDescendantItemStates(childId, cache, descendents);
  +                 collectDescendantItemStates(childId, descendents);
                }
            }
        } catch (RepositoryException re) {
  @@ -78,6 +78,86 @@
        }
       }
   
  +    private void collectDescendantItemStatesInAttic(ItemId id, List descendents) {
  +     try {
  +         if (id.denotesNode()) {
  +             NodeId parentId = (NodeId) id;
  +
  +             // traverse zombie children (i.e. children marked as removed)
  +             ItemId[] childIds = hierMgr.listZombieChildren(parentId);
  +             for (int i = 0; i < childIds.length; i++) {
  +                 ItemId childId = childIds[i];
  +                 // check attic
  +                 if (attic.isCached(childId)) {
  +                     // found on attic, add to descendents list
  +                     descendents.add(attic.retrieve(childId));
  +                 }
  +                 // recurse
  +                 collectDescendantItemStatesInAttic(childId, descendents);
  +             }
  +
  +             // traverse existing children (because parentId might denote
  +             // a node that was marked removed)
  +             childIds = hierMgr.listChildren(parentId);
  +             for (int i = 0; i < childIds.length; i++) {
  +                 ItemId childId = childIds[i];
  +                 // check attic
  +                 if (attic.isCached(childId)) {
  +                     // found on attic, add to descendents list
  +                     descendents.add(attic.retrieve(childId));
  +                 }
  +                 // recurse
  +                 collectDescendantItemStatesInAttic(childId, descendents);
  +             }
  +/*
  +             // try transient cache
  +             NodeState parent = (NodeState) retrieve(parentId);
  +             if (parent == null) {
  +                 // try attic
  +                 parent = (NodeState) attic.retrieve(parentId);
  +                 if (parent == null) {
  +                     log.warn("inconsistent hierarchy state");
  +                     return;
  +                 }
  +             }
  +             // check properties:
  +             // get removed property entries
  +             Iterator iter = parent.getRemovedPropertyEntries().iterator();
  +             while (iter.hasNext()) {
  +                 NodeState.PropertyEntry entry = (NodeState.PropertyEntry) 
iter.next();
  +                 PropertyId propId = new PropertyId(parent.getUUID(), 
entry.getName());
  +                 // check attic
  +                 if (attic.isCached(propId)) {
  +                     // found on attic, add to descendents list
  +                     descendents.add(attic.retrieve(propId));
  +                 }
  +             }
  +             // check child nodes
  +             ArrayList nodeEntries = new ArrayList();
  +             // collect existing child node entries
  +             nodeEntries.addAll(parent.getChildNodeEntries());
  +             // collect removed child node entries
  +             nodeEntries.addAll(parent.getRemovedChildNodeEntries());
  +
  +             iter = nodeEntries.iterator();
  +             while (iter.hasNext()) {
  +                 NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) 
iter.next();
  +                 NodeId nodeId = new NodeId(entry.getUUID());
  +                 // check attic
  +                 if (attic.isCached(nodeId)) {
  +                     // found on attic, add to descendents list
  +                     descendents.add(attic.retrieve(nodeId));
  +                 }
  +                 // recurse
  +                 collectDescendantItemStatesInAttic(nodeId, descendents);
  +             }
  +*/
  +         }
  +     } catch (RepositoryException re) {
  +         log.warn("inconsistent hierarchy state", re);
  +     }
  +    }
  +
       /**
        * Dumps the state of this <code>TransientItemStateManager</code> instance
        * (used for diagnostic purposes).
  @@ -182,8 +262,9 @@
        * @return an iterator over descendant instances
        */
       public Iterator getDescendantItemStates(ItemId parentId) {
  +     // @todo need a more efficient way to find descendents in cache (e.g. using 
hierarchical index)
        ArrayList descendents = new ArrayList();
  -     collectDescendantItemStates(parentId, this, descendents);
  +     collectDescendantItemStates(parentId, descendents);
        return Collections.unmodifiableList(descendents).iterator();
       }
   
  @@ -195,11 +276,25 @@
        * @return an iterator over descendant instances in the attic
        */
       public Iterator getDescendantItemStatesInAttic(ItemId parentId) {
  +     // @todo need a more efficient way to find descendents in attic (e.g. using 
hierarchical index)
        ArrayList descendents = new ArrayList();
  -     collectDescendantItemStates(parentId, attic, descendents);
  +     collectDescendantItemStatesInAttic(parentId, descendents);
        return Collections.unmodifiableList(descendents).iterator();
       }
   
  +    /**
  +     * @param id
  +     * @return
  +     * @throws NoSuchItemStateException
  +     */
  +    public ItemState getItemStateInAttic(ItemId id) throws NoSuchItemStateException 
{
  +     ItemState state = attic.retrieve(id);
  +     if (state != null) {
  +         return state;
  +     } else {
  +         throw new NoSuchItemStateException(id.toString());
  +     }
  +    }
       //----------------< methods for creating & discarding ItemState instances >
       /**
        * @param uuid
  @@ -296,7 +391,6 @@
        * Disposes the specified instance, i.e. discards it and clears it from cache.
        *
        * @param state the <code>ItemState</code> instance that should be disposed
  -     *
        * @see ItemState#discard()
        */
       public void disposeItemState(ItemState state) {
  @@ -327,7 +421,6 @@
        * removes it from the attic.
        *
        * @param state the <code>ItemState</code> instance that should be disposed
  -     *
        * @see ItemState#discard()
        */
       public void disposeItemStateInAttic(ItemState state) {
  
  
  
  1.2       +15 -5     
jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/state/xml/XMLPersistenceManager.java
  
  Index: XMLPersistenceManager.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/core/state/xml/XMLPersistenceManager.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- XMLPersistenceManager.java        28 May 2004 18:23:48 -0000      1.1
  +++ XMLPersistenceManager.java        30 May 2004 21:40:40 -0000      1.2
  @@ -808,11 +808,13 @@
        String nodeFilePath = buildNodeFilePath(uuid);
        FileSystemResource nodeFile = new FileSystemResource(itemStateStore, 
nodeFilePath);
        try {
  -         String folderPath = nodeFile.getParentDir();
            nodeFile.delete();
  -         if (!itemStateStore.hasChildren(folderPath)) {
  -             // prune empty folder
  -             itemStateStore.deleteFolder(folderPath);
  +         // prune empty folders
  +         String parentDir = FileSystemPathUtil.getParentDir(nodeFilePath);
  +         while (!parentDir.equals(FileSystem.SEPARATOR) &&
  +                 !itemStateStore.hasChildren(parentDir)) {
  +             itemStateStore.deleteFolder(parentDir);
  +             parentDir = FileSystemPathUtil.getParentDir(parentDir);
            }
        } catch (FileSystemException fse) {
            String msg = "failed to delete node state: " + uuid;
  @@ -842,8 +844,16 @@
        }
        // delete property xml file
        String propFilePath = buildPropFilePath(state.getParentUUID(), 
state.getName());
  +     FileSystemResource propFile = new FileSystemResource(itemStateStore, 
propFilePath);
        try {
  -         itemStateStore.deleteFile(propFilePath);
  +         propFile.delete();
  +         // prune empty folders
  +         String parentDir = FileSystemPathUtil.getParentDir(propFilePath);
  +         while (!parentDir.equals(FileSystem.SEPARATOR) &&
  +                 !itemStateStore.hasChildren(parentDir)) {
  +             itemStateStore.deleteFolder(parentDir);
  +             parentDir = FileSystemPathUtil.getParentDir(parentDir);
  +         }
        } catch (FileSystemException fse) {
            String msg = "failed to delete property state: " + state.getParentUUID() + 
"/" + state.getName();
            log.error(msg, fse);
  
  
  
  1.7       +26 -0     
jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/fs/FileSystemPathUtil.java
  
  Index: FileSystemPathUtil.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/fs/FileSystemPathUtil.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- FileSystemPathUtil.java   28 May 2004 18:23:48 -0000      1.6
  +++ FileSystemPathUtil.java   30 May 2004 21:40:40 -0000      1.7
  @@ -145,4 +145,30 @@
        }
        return new String(out.toByteArray());
       }
  +
  +    /**
  +     * Returns the parent directory of the specified <code>path</code>.
  +     * @param path a file system path denoting a directory or a file.
  +     * @return the parent directory.
  +     */
  +    public static String getParentDir(String path) {
  +        int pos = path.lastIndexOf(FileSystem.SEPARATOR_CHAR);
  +        if (pos > 0) {
  +            return path.substring(0, pos);
  +        }
  +        return FileSystem.SEPARATOR;
  +    }
  +
  +    /**
  +     * Returns the name of the specified <code>path</code>.
  +     * @param path a file system path denoting a directory or a file.
  +     * @return the name.
  +     */
  +    public static String getName(String path) {
  +        int pos = path.lastIndexOf(FileSystem.SEPARATOR_CHAR);
  +        if (pos != -1) {
  +            return path.substring(pos + 1);
  +        }
  +        return path;
  +    }
   }
  
  
  
  1.2       +6 -12     
jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/fs/FileSystemResource.java
  
  Index: FileSystemResource.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-slide/proposals/jcrri/src/org/apache/slide/jcr/fs/FileSystemResource.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- FileSystemResource.java   28 May 2004 18:23:48 -0000      1.1
  +++ FileSystemResource.java   30 May 2004 21:40:40 -0000      1.2
  @@ -75,25 +75,19 @@
       }
   
       /**
  -     * Return the parent directory of this resource.
  +     * Returns the parent directory of this resource.
  +     * @return the parent directory.
        */
       public String getParentDir() {
  -        int pos = path.lastIndexOf(FileSystem.SEPARATOR_CHAR);
  -        if (pos > 0) {
  -            return path.substring(0, pos);
  -        }
  -        return FileSystem.SEPARATOR;
  +        return FileSystemPathUtil.getParentDir(path);
       }
   
       /**
  -     * Return the name of this resource
  +     * Returns the name of this resource.
  +     * @return the name.
        */
       public String getName() {
  -        int pos = path.lastIndexOf(FileSystem.SEPARATOR_CHAR);
  -        if (pos != -1) {
  -            return path.substring(pos + 1);
  -        }
  -        return path;
  +     return FileSystemPathUtil.getName(path);
       }
   
       /**
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to