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 final String     m_name;
043    private final 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;
049    private final Map< String, Object > m_attributes = new HashMap<>();
050
051    private Acl m_accessList;
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;
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        p.m_attributes.putAll( m_attributes );
320
321        if( m_accessList != null ) {
322            p.m_accessList = new AclImpl();
323            for( final Enumeration< AclEntry > entries = m_accessList.entries(); entries.hasMoreElements(); ) {
324                final AclEntry e = entries.nextElement();
325                p.m_accessList.addEntry( e );
326            }
327        }
328            
329        return p;
330    }
331    
332    /**
333     *  Compares a page with another by name using the defined PageNameComparator.  If the same name, compares their versions.
334     *  
335     *  @param page The page to compare against
336     *  @return -1, 0 or 1
337     */
338    @Override
339    public int compareTo( final Page page ) {
340        if( this == page ) {
341            return 0; // the same object
342        }
343
344        int res = m_engine.getManager( PageManager.class ).getPageSorter().compare( this.getName(), page.getName() );
345        if( res == 0 ) {
346            res = this.getVersion() - page.getVersion();
347        }
348        return res;
349    }
350    
351    /**
352     *  A page is equal to another page if its name and version are equal.
353     *  
354     *  {@inheritDoc}
355     */
356    public boolean equals( final Object o ) {
357        if( o instanceof WikiPage ) {
358            final WikiPage wp = ( WikiPage )o;
359            if( wp.getName().equals( getName() ) ) {
360                return wp.getVersion() == getVersion();
361            }
362        }
363
364        return false;
365    }
366
367    /**
368     * {@inheritDoc}
369     */
370    @Override
371    public int hashCode() {
372        return m_name.hashCode() * m_version;
373    }
374
375}