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