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