001/*
002    Licensed to the Apache Software Foundation (ASF) under one
003    or more contributor license agreements.  See the NOTICE file
004    distributed with this work for additional information
005    regarding copyright ownership.  The ASF licenses this file
006    to you under the Apache License, Version 2.0 (the
007    "License"); you may not use this file except in compliance
008    with the License.  You may obtain a copy of the License at
009
010       http://www.apache.org/licenses/LICENSE-2.0
011
012    Unless required by applicable law or agreed to in writing,
013    software distributed under the License is distributed on an
014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015    KIND, either express or implied.  See the License for the
016    specific language governing permissions and limitations
017    under the License.    
018 */
019package org.apache.wiki;
020
021import java.util.Date;
022import java.util.Enumeration;
023import java.util.HashMap;
024import java.util.Map;
025
026import org.apache.wiki.auth.acl.Acl;
027import org.apache.wiki.auth.acl.AclEntry;
028import org.apache.wiki.auth.acl.AclImpl;
029import org.apache.wiki.providers.WikiPageProvider;
030
031/**
032 *  Simple wrapper class for the Wiki page attributes.  The Wiki page
033 *  content is moved around in Strings, though.
034 */
035
036// FIXME: We need to rethink how metadata is being used - probably the 
037//        author, date, etc. should also be part of the metadata.  We also
038//        need to figure out the metadata lifecycle.
039
040public class WikiPage
041    implements Cloneable,
042               Comparable
043{
044    private static final long serialVersionUID = 1L;
045
046    private       String     m_name;
047    private       WikiEngine m_engine;
048    private       String     m_wiki;
049    private Date             m_lastModified;
050    private long             m_fileSize = -1;
051    private int              m_version = WikiPageProvider.LATEST_VERSION;
052    private String           m_author = null;
053    private final HashMap<String,Object> m_attributes = new HashMap<String,Object>();
054
055    /**
056     *  "Summary" is a short summary of the page.  It is a String.
057     */
058    public static final String DESCRIPTION = "summary";
059
060    /** A special variable name for storing a page alias. */
061    public static final String ALIAS = "alias";
062    
063    /** A special variable name for storing a redirect note */
064    public static final String REDIRECT = "redirect";
065
066    /** A special variable name for storing the author. */
067    public static final String AUTHOR = "author";
068    
069    /** A special variable name for storing a changenote. */
070    public static final String CHANGENOTE = "changenote";
071
072    /** A special variable name for storing a viewcount. */
073    public static final String VIEWCOUNT = "viewcount";
074    
075    private Acl m_accessList = null;
076    
077    /**
078     *  Create a new WikiPage using a given engine and name.
079     *  
080     *  @param engine The WikiEngine that owns this page.
081     *  @param name   The name of the page.
082     */
083    public WikiPage( WikiEngine engine, String name )
084    {
085        m_engine = engine;
086        m_name = name;
087        m_wiki = engine.getApplicationName();
088    }
089
090    /**
091     *  Returns the name of the page.
092     *  
093     *  @return The page name.
094     */
095    public String getName()
096    {
097        return m_name;
098    }
099
100    /**
101     *  A WikiPage may have a number of attributes, which might or might not be 
102     *  available.  Typically attributes are things that do not need to be stored
103     *  with the wiki page to the page repository, but are generated
104     *  on-the-fly.  A provider is not required to save them, but they
105     *  can do that if they really want.
106     *
107     *  @param key The key using which the attribute is fetched
108     *  @return The attribute.  If the attribute has not been set, returns null.
109     */
110    public Object getAttribute( String key )
111    {
112        return m_attributes.get( key );
113    }
114
115    /**
116     *  Sets an metadata attribute.
117     *  
118     *  @see #getAttribute(String)
119     *  @param key The key for the attribute used to fetch the attribute later on.
120     *  @param attribute The attribute value
121     */
122    public void setAttribute( String key, Object attribute )
123    {
124        m_attributes.put( key, attribute );
125    }
126
127    /**
128     * Returns the full attributes Map, in case external code needs
129     * to iterate through the attributes.
130     * 
131     * @return The attribute Map.  Please note that this is a direct
132     *         reference, not a copy.
133     */
134    public Map getAttributes() 
135    {
136        return m_attributes;
137    }
138
139    /**
140     *  Removes an attribute from the page, if it exists.
141     *  
142     *  @param  key The key for the attribute
143     *  @return If the attribute existed, returns the object.
144     *  @since 2.1.111
145     */
146    public Object removeAttribute( String key )
147    {
148        return m_attributes.remove( key );
149    }
150
151    /**
152     *  Returns the date when this page was last modified.
153     *  
154     *  @return The last modification date
155     */
156    public Date getLastModified()
157    {
158        return m_lastModified;
159    }
160
161    /**
162     *  Sets the last modification date.  In general, this is only
163     *  changed by the provider.
164     *  
165     *  @param date The date
166     */
167    public void setLastModified( Date date )
168    {
169        m_lastModified = date;
170    }
171
172    /**
173     *  Sets the page version.  In general, this is only changed
174     *  by the provider.
175     *  
176     *  @param version The version number
177     */
178    public void setVersion( int version )
179    {
180        m_version = version;
181    }
182
183    /**
184     *  Returns the version that this WikiPage instance represents.
185     *  
186     *  @return the version number of this page.
187     */
188    public int getVersion()
189    {
190        return m_version;
191    }
192
193    /**
194     *  Returns the size of the page.
195     *  
196     *  @return the size of the page. 
197     *  @since 2.1.109
198     */
199    public long getSize()
200    {
201        return m_fileSize;
202    }
203
204    /**
205     *  Sets the size.  Typically called by the provider only.
206     *  
207     *  @param size The size of the page.
208     *  @since 2.1.109
209     */
210    public void setSize( long size )
211    {
212        m_fileSize = size;
213    }
214
215    /**
216     *  Returns the Acl for this page.  May return <code>null</code>, 
217     *  in case there is no Acl defined, or it has not
218     *  yet been set by {@link #setAcl(Acl)}.
219     *  
220     *  @return The access control list.  May return null, if there is 
221     *          no acl.
222     */
223    public Acl getAcl()
224    {
225        return m_accessList;
226    }
227
228    /**
229     * Sets the Acl for this page. Note that method does <em>not</em>
230     * persist the Acl itself to back-end storage or in page markup;
231     * it merely sets the internal field that stores the Acl. To
232     * persist the Acl, callers should invoke 
233     * {@link org.apache.wiki.auth.acl.AclManager#setPermissions(WikiPage, Acl)}.
234     * @param acl The Acl to set
235     */
236    public void setAcl( Acl acl )
237    {
238        m_accessList = acl;
239    }
240
241    /**
242     *  Sets the author of the page.  Typically called only by the provider.
243     *  
244     *  @param author The author name.
245     */
246    public void setAuthor( String author )
247    {
248        m_author = author;
249    }
250
251    /**
252     *  Returns author name, or null, if no author has been defined.
253     *  
254     *  @return Author name, or possibly null.
255     */
256    public String getAuthor()
257    {
258        return m_author;
259    }
260    
261    /**
262     *  Returns the wiki name for this page
263     *  
264     *  @return The name of the wiki.
265     */
266    public String getWiki()
267    {
268        return m_wiki;
269    }
270
271    /**
272     *  This method will remove all metadata from the page.
273     */
274    public void invalidateMetadata()
275    {        
276        m_hasMetadata = false;
277        setAcl( null );
278        m_attributes.clear();
279    }
280
281    private boolean m_hasMetadata = false;
282
283    /**
284     *  Returns <code>true</code> if the page has valid metadata; that is, it has been parsed.
285     *  Note that this method is a kludge to support our pre-3.0 metadata system, and as such
286     *  will go away with the new API.
287     *  
288     *  @return true, if the page has metadata.
289     */
290    public boolean hasMetadata()
291    {
292        return m_hasMetadata;
293    }
294
295    /**
296     *  Sets the metadata flag to true.  Never call.
297     */
298    public void setHasMetadata()
299    {
300        m_hasMetadata = true;
301    }
302
303    /**
304     *  Returns a debug-suitable version of the page.
305     *  
306     *  @return A debug string.
307     */
308    public String toString()
309    {
310        return "WikiPage ["+m_wiki+":"+m_name+",ver="+m_version+",mod="+m_lastModified+"]";
311    }
312
313    /**
314     *  Creates a deep clone of a WikiPage.  Strings are not cloned, since
315     *  they're immutable.  Attributes are not cloned, only the internal
316     *  HashMap (so if you modify the contents of a value of an attribute,
317     *  these will reflect back to everyone).
318     *  
319     *  @return A deep clone of the WikiPage
320     */
321    public Object clone()
322    {
323        WikiPage p = new WikiPage( m_engine, m_name );
324       
325        p.m_wiki         = m_wiki;
326            
327        p.m_author       = m_author;
328        p.m_version      = m_version;
329        p.m_lastModified = m_lastModified != null ? (Date)m_lastModified.clone() : null;
330
331        p.m_fileSize     = m_fileSize;
332
333        for( Map.Entry<String,Object> entry : m_attributes.entrySet() )
334        {
335            p.m_attributes.put( entry.getKey(), 
336                                entry.getValue() );
337        }
338
339        if( m_accessList != null )
340        {
341            p.m_accessList = new AclImpl();
342            
343            for( Enumeration< AclEntry > entries = m_accessList.entries(); entries.hasMoreElements(); )
344            {
345                AclEntry e = entries.nextElement();
346            
347                p.m_accessList.addEntry( e );
348            }
349        }
350            
351        return p;
352    }
353    
354    /**
355     *  Compares a page with another using the defined PageNameComparator.  See org.apache.wiki.util.PageSorter.
356     *  
357     *  @param page The object to compare against
358     *  @return -1, 0 or 1
359     */
360    public int compareTo( Object page )
361    {
362        return m_engine.getPageSorter().compare( this, (WikiPage) page );
363    }
364    
365    /**
366     *  A page is equal to another page if its name and version are equal.
367     *  
368     *  {@inheritDoc}
369     */
370    // TODO: I have a suspicion that defining this method causes some problems
371    //       with page attributes and caching.  So as of 2.7.32, it's disabled.
372    /*
373    public boolean equals( Object o )
374    {
375        if( o != null && o instanceof WikiPage )
376        {
377            WikiPage oo = (WikiPage) o;
378        
379            if( oo.getName().equals( getName() ) )
380            {
381                if( oo.getVersion() == getVersion() )
382                {
383                    return true;
384                }
385            }
386        }
387        
388        return false;
389    }
390    */
391    /**
392     *  {@inheritDoc}
393     */
394    public int hashCode()
395    {
396        return m_name.hashCode() * m_version;
397    }
398}