<commit_before>package imcode.server.document;

import com.imcode.imcms.flow.DocumentPageFlow;
import imcode.server.*;
import imcode.server.document.index.AutorebuildingDirectoryIndex;
import imcode.server.document.index.DocumentIndex;
import imcode.server.document.textdocument.MenuItemDomainObject;
import imcode.server.document.textdocument.TextDocumentDomainObject;
import imcode.server.document.textdocument.TextDomainObject;
import imcode.server.user.ImcmsAuthenticatorAndUserAndRoleMapper;
import imcode.server.user.RoleDomainObject;
import imcode.server.user.UserDomainObject;
import imcode.util.*;
import org.apache.commons.collections.map.AbstractMapDecorator;
import org.apache.commons.collections.map.LRUMap;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.UnhandledException;
import org.apache.commons.lang.math.IntRange;
import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
import org.apache.oro.text.perl.Perl5Util;

import java.io.File;
import java.io.FileFilter;
import java.lang.ref.SoftReference;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class DocumentMapper {

    private final static Logger log = Logger.getLogger( DocumentMapper.class.getName() );

    private static final int UNLIMITED_MAX_CATEGORY_CHOICES = 0;

    private static final int META_HEADLINE_MAX_LENGTH = 255;
    private static final int META_TEXT_MAX_LENGTH = 1000;

    // Stored procedure names used in this class
    // todo make sure all these is only used in one sprocMethod
    private static final String SPROC_SECTION_GET_INHERIT_ID = "SectionGetInheritId";
    private static final String SPROC_GET_DOCUMENT_INFO = "GetDocumentInfo";
    private static final String SPROC_GET_TEXT = "GetText";
    private static final String SPROC_UPDATE_PARENTS_DATE_MODIFIED = "UpdateParentsDateModified";
    private static final String SPROC_SECTION_GET_ALL = "SectionGetAll";
    private static final String SPROC_GET_DOC_TYPES_FOR_USER = "GetDocTypesForUser";

    private ImcmsServices service;
    private DocumentPermissionSetMapper documentPermissionSetMapper;
    private DocumentIndex documentIndex;

    private static final int DOCUMENT_CACHE_MAX_SIZE = 100;

    private DocumentCache documentCache = new DocumentCache( new LRUMap( DOCUMENT_CACHE_MAX_SIZE ), this ) ;

    public DocumentMapper( ImcmsServices service ) {
        this.service = service;
        documentPermissionSetMapper = new DocumentPermissionSetMapper( service );
        File webAppPath = WebAppGlobalConstants.getInstance().getAbsoluteWebAppPath();
        File indexDirectory = new File( webAppPath, "WEB-INF/index" );

        this.documentIndex = new AutorebuildingDirectoryIndex( indexDirectory, service.getConfig().getIndexingSchedulePeriodInMinutes() );
    }

    public DocumentDomainObject createDocumentOfTypeFromParent( int documentTypeId, final DocumentDomainObject parent,
                                                                UserDomainObject user ) {
        if ( !user.canCreateDocumentOfTypeIdFromParent( documentTypeId, parent ) ) {
            throw new SecurityException( "User can't create documents from document "+parent.getId() ) ;
        }
        DocumentDomainObject newDocument;
        try {
            if ( DocumentDomainObject.DOCTYPE_ID_TEXT == documentTypeId ) {
                newDocument = (DocumentDomainObject)parent.clone();
                TextDocumentDomainObject newTextDocument = (TextDocumentDomainObject)newDocument;
                newTextDocument.removeAllTexts();
                newTextDocument.removeAllImages();
                newTextDocument.removeAllIncludes();
                newTextDocument.removeAllMenus();
                int permissionSetId = user.getPermissionSetIdFor( parent );
                TemplateDomainObject template = null;
                if ( DocumentPermissionSetDomainObject.TYPE_ID__RESTRICTED_1 == permissionSetId ) {
                    template = ((TextDocumentPermissionSetDomainObject)newTextDocument.getPermissionSetForRestrictedOneForNewDocuments()).getDefaultTemplate();
                } else if ( DocumentPermissionSetDomainObject.TYPE_ID__RESTRICTED_2 == permissionSetId ) {
                    template = ( (TextDocumentPermissionSetDomainObject)newTextDocument.getPermissionSetForRestrictedTwoForNewDocuments() ).getDefaultTemplate();
                } else if ( parent instanceof TextDocumentDomainObject ) {
                    template = ((TextDocumentDomainObject)parent).getDefaultTemplate() ;
                }
                if ( null != template ) {
                    newTextDocument.setTemplate( template );
                }
            } else {
                newDocument = DocumentDomainObject.fromDocumentTypeId( documentTypeId );
                newDocument.setAttributes( (DocumentDomainObject.Attributes)parent.getAttributes().clone() );
            }
        } catch ( CloneNotSupportedException e ) {
            throw new UnhandledException( e );
        }
        newDocument.setId( 0 );
        newDocument.setCreator( user );
        newDocument.setStatus( DocumentDomainObject.STATUS_NEW );
        newDocument.setHeadline( "" );
        newDocument.setMenuText( "" );
        newDocument.setMenuImage( "" );
        newDocument.setPublicationStartDatetime( new Date() );
        newDocument.setArchivedDatetime( null );
        newDocument.setPublicationEndDatetime( null );
        return newDocument;
    }

    public CategoryDomainObject[] getAllCategoriesOfType( CategoryTypeDomainObject categoryType ) {
        String sqlQuery = "SELECT categories.category_id, categories.name, categories.description, categories.image\n"
                          + "FROM categories\n"
                          + "JOIN category_types ON categories.category_type_id = category_types.category_type_id\n"
                          + "WHERE categories.category_type_id = ?";
        String[][] sqlResult = service.sqlQueryMulti( sqlQuery, new String[]{"" + categoryType.getId()} );
        CategoryDomainObject[] categoryDomainObjects = new CategoryDomainObject[sqlResult.length];
        for ( int i = 0; i < sqlResult.length; i++ ) {
            int categoryId = Integer.parseInt( sqlResult[i][0] );
            String categoryName = sqlResult[i][1];
            String categoryDescription = sqlResult[i][2];
            String categoryImage = sqlResult[i][3];

            categoryDomainObjects[i] =
            new CategoryDomainObject( categoryId, categoryName, categoryDescription, categoryImage, categoryType );
        }
        return categoryDomainObjects;
    }

    public boolean isUniqueCategoryTypeName( String categoryTypeName ) {
        CategoryTypeDomainObject[] categoryTypes = getAllCategoryTypes();
        for ( int i = 0; i < categoryTypes.length; i++ ) {
            CategoryTypeDomainObject categoryType = categoryTypes[i];
            if ( categoryType.getName().equalsIgnoreCase( categoryTypeName ) ) {
                return false;
            }
        }
        return true;
    }

    public CategoryTypeDomainObject[] getAllCategoryTypes() {
        String sqlQuery = "SELECT category_type_id, name, max_choices FROM category_types ORDER BY name";
        String[][] sqlResult = service.sqlQueryMulti( sqlQuery, new String[0] );

        CategoryTypeDomainObject[] categoryTypeDomainObjects = new CategoryTypeDomainObject[sqlResult.length];
        for ( int i = 0; i < categoryTypeDomainObjects.length; i++ ) {
            int categoryTypeId = Integer.parseInt( sqlResult[i][0] );
            String typeName = sqlResult[i][1];
            int maxChoices = Integer.parseInt( sqlResult[i][2] );

            categoryTypeDomainObjects[i] = new CategoryTypeDomainObject( categoryTypeId, typeName, maxChoices );
        }

        return categoryTypeDomainObjects;
    }

    public SectionDomainObject[] getAllSections() {
        String[][] sqlRows = service.sqlProcedureMulti( SPROC_SECTION_GET_ALL, new String[0] );
        SectionDomainObject[] allSections = new SectionDomainObject[sqlRows.length];
        for ( int i = 0; i < sqlRows.length; i++ ) {
            int sectionId = Integer.parseInt( sqlRows[i][0] );
            String sectionName = sqlRows[i][1];
            allSections[i] = new SectionDomainObject( sectionId, sectionName );
        }
        Arrays.sort(allSections, new SectionNameComparator()) ;
        return allSections;
    }

    public CategoryDomainObject getCategory( CategoryTypeDomainObject categoryType, String categoryName ) {
        String sqlQuery = "SELECT categories.category_id, categories.name, categories.description, categories.image\n"
                          + "FROM categories\n"
                          + "JOIN category_types\n"
                          + "ON categories.category_type_id = category_types.category_type_id\n"
                          + "WHERE category_types.name = ?\n"
                          + "AND categories.name = ?";
        String[] sqlResult = service.sqlQuery( sqlQuery, new String[]{categoryType.getName(), categoryName} );
        if ( 0 != sqlResult.length ) {
            final int categoryId = Integer.parseInt( sqlResult[0] );
            final String categoryNameFromDb = sqlResult[1];
            final String categoryDescription = sqlResult[2];
            final String categoryImge = sqlResult[3];

            return new CategoryDomainObject( categoryId, categoryNameFromDb, categoryDescription, categoryImge,
                                             categoryType );
        } else {
            return null;
        }
    }

    public CategoryDomainObject getCategoryById( int categoryId ) {
        String sqlQuery = "SELECT categories.name, categories.description, categories.image, category_types.category_type_id, category_types.name, category_types.max_choices\n"
                          + "FROM categories\n"
                          + "JOIN category_types ON categories.category_type_id = category_types.category_type_id\n"
                          + "WHERE categories.category_id = ?";

        String[] categorySqlResult = service.sqlQuery( sqlQuery, new String[]{"" + categoryId} );

        if ( 0 != categorySqlResult.length ) {
            String categoryName = categorySqlResult[0];
            String categoryDescription = categorySqlResult[1];
            String categoryImage = categorySqlResult[2];
            int categoryTypeId = Integer.parseInt( categorySqlResult[3] );
            String categoryTypeName = categorySqlResult[4];
            int categoryTypeMaxChoices = Integer.parseInt( categorySqlResult[5] );

            CategoryTypeDomainObject categoryType = new CategoryTypeDomainObject( categoryTypeId, categoryTypeName,
                                                                                  categoryTypeMaxChoices );

            return new CategoryDomainObject( categoryId, categoryName, categoryDescription, categoryImage, categoryType );
        } else {
            return null;
        }
    }

    public CategoryTypeDomainObject getCategoryType( String categoryTypeName ) {
        String sqlStr = "SELECT category_types.category_type_id, category_types.name, category_types.max_choices\n"
                        + "FROM category_types\n"
                        + "WHERE category_types.name = ?";
        String[] sqlResult = service.sqlQuery( sqlStr, new String[]{categoryTypeName} );

        if ( null == sqlResult || 0 == sqlResult.length ) {
            return null;
        } else {
            int categoryTypeId = Integer.parseInt( sqlResult[0] );
            String categoryTypeNameFromDb = sqlResult[1];
            int categoryTypeMaxChoices = Integer.parseInt( sqlResult[2] );
            return new CategoryTypeDomainObject( categoryTypeId, categoryTypeNameFromDb, categoryTypeMaxChoices );
        }
    }

    public CategoryTypeDomainObject getCategoryTypeById( int categoryTypeId ) {
        String sqlStr = "select name, max_choices  from category_types where category_type_id = ? ";
        String[] sqlResult = service.sqlQuery( sqlStr, new String[]{"" + categoryTypeId} );

        if ( null == sqlResult || 0 == sqlResult.length ) {
            return null;
        } else {
            String categoryTypeNameFromDb = sqlResult[0];
            int categoryTypeMaxChoices = Integer.parseInt( sqlResult[1] );
            return new CategoryTypeDomainObject( categoryTypeId, categoryTypeNameFromDb, categoryTypeMaxChoices );
        }
    }

    public void deleteCategoryTypeFromDb( CategoryTypeDomainObject categoryType ) {
        String sqlstr = "delete from category_types where category_type_id = ?";
        service.sqlUpdateQuery( sqlstr, new String[]{categoryType.getId() + ""} );
    }

    public CategoryTypeDomainObject addCategoryTypeToDb( String name, int max_choices ) {
        String sqlstr = "insert into category_types (name, max_choices) values(?,?) SELECT @@IDENTITY";
        String newId = service.sqlQueryStr( sqlstr, new String[]{name, max_choices + ""} );
        return getCategoryTypeById( Integer.parseInt( newId ) );
    }

    public void updateCategoryType( CategoryTypeDomainObject categoryType ) {
        String sqlstr = "update category_types set name= ?, max_choices= ?  where category_type_id = ? ";
        service.sqlUpdateQuery( sqlstr, new String[]{
            categoryType.getName(), categoryType.getMaxChoices() + "", categoryType.getId() + ""
        } );
    }

    public CategoryDomainObject addCategoryToDb( int categoryTypeId, String name, String description, String image ) {
        String sqlstr = "insert into categories  (category_type_id, name, description, image) values(?,?,?,?) SELECT @@IDENTITY";
        String newId = service.sqlQueryStr( sqlstr, new String[]{
            categoryTypeId + "", name, description, image
        } );
        return getCategoryById( Integer.parseInt( newId ) );
    }

    public void updateCategory( CategoryDomainObject category ) {
        String sqlstr = "update categories set category_type_id = ?, name= ?, description = ?, image = ?  where category_id = ? ";
        service.sqlUpdateQuery( sqlstr, new String[]{
            category.getType().getId() + "", category.getName(), category.getDescription(), category.getImageUrl(),
            category.getId() + ""
        } );
    }

    public void deleteCategoryFromDb( CategoryDomainObject category ) {
        String sqlstr = "delete from categories where category_id = ?";
        service.sqlUpdateQuery( sqlstr, new String[]{category.getId() + ""} );
    }

    public DocumentDomainObject getDocument( int metaId ) {
        NDC.push( "getDocument" );

        DocumentDomainObject document ;
        try {
            document = (DocumentDomainObject)documentCache.get( new Integer( metaId ) );
            document = (DocumentDomainObject)document.clone();
        } catch ( CloneNotSupportedException e ) {
            throw new UnhandledException( e );
        }

        NDC.pop();
        return document;
    }

    public DocumentReference getDocumentReference( DocumentDomainObject document ) {
        return getDocumentReference( document.getId() ) ;
    }

    DocumentReference getDocumentReference( int childId ) {
        return new DocumentReference( childId, this );
    }

    private DocumentDomainObject getDocumentFromDb( int metaId ) {
        NDC.push( "getDocumentFromDb" );
        log.debug( "Getting document " + metaId + " from db." );

        String[] result = sprocGetDocumentInfo( metaId );

        DocumentDomainObject document = null;
        if ( 0 != result.length ) {
            document = getDocumentFromSqlResultRow( result );
            initDocumentAttributes( document ) ;
            initDocumentCategories( document );
            initRolesMappedToDocumentPermissionSetIds( document );

            document.accept( new DocumentInitializingVisitor() );
        }
        NDC.pop();
        return document;
    }

    public void initDocumentAttributes( DocumentDomainObject document ) {

        document.setSections( getSections( document.getId() ) );

        document.setKeywords( getKeywords( document.getId() ) );

        document.setPermissionSetForRestrictedOne( documentPermissionSetMapper.getPermissionSetRestrictedOne( document ) );
        document.setPermissionSetForRestrictedTwo( documentPermissionSetMapper.getPermissionSetRestrictedTwo( document ) );

        document.setPermissionSetForRestrictedOneForNewDocuments( documentPermissionSetMapper.getPermissionSetRestrictedOneForNewDocuments( document ) );
        document.setPermissionSetForRestrictedTwoForNewDocuments( documentPermissionSetMapper.getPermissionSetRestrictedTwoForNewDocuments( document ) );

    }

    public void initDocumentCategories( DocumentDomainObject document ) {

        addCategoriesFromDatabaseToDocument( document );

    }

    public void initRolesMappedToDocumentPermissionSetIds( DocumentDomainObject document ) {

        String[][] sprocResult = service.sqlQueryMulti( "SELECT "+ImcmsAuthenticatorAndUserAndRoleMapper.SQL_ROLES_COLUMNS+", rr.set_id\n"
                                                        + "FROM  roles, roles_rights AS rr\n"
                                                        + "WHERE rr.role_id = roles.role_id AND rr.meta_id = ?",
                                                        new String[]{"" + document.getId()} );

        ImcmsAuthenticatorAndUserAndRoleMapper imcmsAuthenticatorAndUserAndRoleMapper = service.getImcmsAuthenticatorAndUserAndRoleMapper();
        for ( int i = 0; i < sprocResult.length; ++i ) {
            RoleDomainObject role = imcmsAuthenticatorAndUserAndRoleMapper.getRoleFromSqlResult(sprocResult[i]) ;

            int rolePermissionSetId = Integer.parseInt( sprocResult[i][4] );
            document.setPermissionSetIdForRole( role, rolePermissionSetId );
        }

    }

    public SectionDomainObject getSectionById( int sectionId ) {
        String sectionName = service.sqlQueryStr( "SELECT section_name FROM sections WHERE section_id = ?",
                                                  new String[]{"" + sectionId} );
        if ( null == sectionName ) {
            return null;
        }
        return new SectionDomainObject( sectionId, sectionName );
    }

    public SectionDomainObject getSectionByName( String name ) {
        String[] sectionSqlRow = service.sqlQuery( "SELECT section_id, section_name FROM sections WHERE section_name = ?",
                                                   new String[]{name} );
        if ( 0 == sectionSqlRow.length ) {
            return null;
        }
        int sectionId = Integer.parseInt( sectionSqlRow[0] );
        String sectionName = sectionSqlRow[1];
        return new SectionDomainObject( sectionId, sectionName );
    }

    /**
     * @return the sections for a document, empty array if there is none.
     */
    private SectionDomainObject[] getSections( int meta_id ) {
        String[][] sectionData = service.sqlProcedureMulti( SPROC_SECTION_GET_INHERIT_ID,
                                                            new String[]{String.valueOf( meta_id )} );

        SectionDomainObject[] sections = new SectionDomainObject[sectionData.length];

        for ( int i = 0; i < sectionData.length; i++ ) {
            int sectionId = Integer.parseInt( sectionData[i][0] );
            String sectionName = sectionData[i][1];
            sections[i] = new SectionDomainObject( sectionId, sectionName );
        }
        return sections;
    }

    public TextDomainObject getText( int metaId, int no ) {
        String[] results = sprocGetText( metaId, no );

        if ( results == null || results.length == 0 ) {
            /* There was no text. Return null. */
            return null;
        }

        /* Return the text */
        String text = results[0];
        int type = Integer.parseInt( results[1] );

        return new TextDomainObject( text, type );

    }

    public void removeInclusion( int includingMetaId, int includeIndex ) {
        sprocDeleteInclude( service, includingMetaId, includeIndex );
    }

    public void saveNewDocument( DocumentDomainObject document, UserDomainObject user )
            throws MaxCategoryDomainObjectsOfTypeExceededException {

        if ( !user.canEdit( document ) ) {
            return; // TODO: More specific check needed. Throw exception ?
        }

        int newMetaId = sqlInsertIntoMeta( document );

        if ( !user.isSuperAdminOrHasFullPermissionOn( document ) ) {
            document.setPermissionSetForRestrictedOne( document.getPermissionSetForRestrictedOneForNewDocuments() );
            document.setPermissionSetForRestrictedTwo( document.getPermissionSetForRestrictedTwoForNewDocuments() );
        }

        document.setId( newMetaId );<commit_msg>Bug 2737: Set created datetime to "now" when copying (or creating) documents.<commit_after>package imcode.server.document;

import com.imcode.imcms.flow.DocumentPageFlow;
import imcode.server.*;
import imcode.server.document.index.AutorebuildingDirectoryIndex;
import imcode.server.document.index.DocumentIndex;
import imcode.server.document.textdocument.MenuItemDomainObject;
import imcode.server.document.textdocument.TextDocumentDomainObject;
import imcode.server.document.textdocument.TextDomainObject;
import imcode.server.user.ImcmsAuthenticatorAndUserAndRoleMapper;
import imcode.server.user.RoleDomainObject;
import imcode.server.user.UserDomainObject;
import imcode.util.*;
import org.apache.commons.collections.map.AbstractMapDecorator;
import org.apache.commons.collections.map.LRUMap;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.UnhandledException;
import org.apache.commons.lang.math.IntRange;
import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
import org.apache.oro.text.perl.Perl5Util;

import java.io.File;
import java.io.FileFilter;
import java.lang.ref.SoftReference;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class DocumentMapper {

    private final static Logger log = Logger.getLogger( DocumentMapper.class.getName() );

    private static final int UNLIMITED_MAX_CATEGORY_CHOICES = 0;

    private static final int META_HEADLINE_MAX_LENGTH = 255;
    private static final int META_TEXT_MAX_LENGTH = 1000;

    // Stored procedure names used in this class
    // todo make sure all these is only used in one sprocMethod
    private static final String SPROC_SECTION_GET_INHERIT_ID = "SectionGetInheritId";
    private static final String SPROC_GET_DOCUMENT_INFO = "GetDocumentInfo";
    private static final String SPROC_GET_TEXT = "GetText";
    private static final String SPROC_UPDATE_PARENTS_DATE_MODIFIED = "UpdateParentsDateModified";
    private static final String SPROC_SECTION_GET_ALL = "SectionGetAll";
    private static final String SPROC_GET_DOC_TYPES_FOR_USER = "GetDocTypesForUser";

    private ImcmsServices service;
    private DocumentPermissionSetMapper documentPermissionSetMapper;
    private DocumentIndex documentIndex;

    private static final int DOCUMENT_CACHE_MAX_SIZE = 100;

    private DocumentCache documentCache = new DocumentCache( new LRUMap( DOCUMENT_CACHE_MAX_SIZE ), this ) ;

    public DocumentMapper( ImcmsServices service ) {
        this.service = service;
        documentPermissionSetMapper = new DocumentPermissionSetMapper( service );
        File webAppPath = WebAppGlobalConstants.getInstance().getAbsoluteWebAppPath();
        File indexDirectory = new File( webAppPath, "WEB-INF/index" );

        this.documentIndex = new AutorebuildingDirectoryIndex( indexDirectory, service.getConfig().getIndexingSchedulePeriodInMinutes() );
    }

    public DocumentDomainObject createDocumentOfTypeFromParent( int documentTypeId, final DocumentDomainObject parent,
                                                                UserDomainObject user ) {
        if ( !user.canCreateDocumentOfTypeIdFromParent( documentTypeId, parent ) ) {
            throw new SecurityException( "User can't create documents from document "+parent.getId() ) ;
        }
        DocumentDomainObject newDocument;
        try {
            if ( DocumentDomainObject.DOCTYPE_ID_TEXT == documentTypeId ) {
                newDocument = (DocumentDomainObject)parent.clone();
                TextDocumentDomainObject newTextDocument = (TextDocumentDomainObject)newDocument;
                newTextDocument.removeAllTexts();
                newTextDocument.removeAllImages();
                newTextDocument.removeAllIncludes();
                newTextDocument.removeAllMenus();
                int permissionSetId = user.getPermissionSetIdFor( parent );
                TemplateDomainObject template = null;
                if ( DocumentPermissionSetDomainObject.TYPE_ID__RESTRICTED_1 == permissionSetId ) {
                    template = ((TextDocumentPermissionSetDomainObject)newTextDocument.getPermissionSetForRestrictedOneForNewDocuments()).getDefaultTemplate();
                } else if ( DocumentPermissionSetDomainObject.TYPE_ID__RESTRICTED_2 == permissionSetId ) {
                    template = ( (TextDocumentPermissionSetDomainObject)newTextDocument.getPermissionSetForRestrictedTwoForNewDocuments() ).getDefaultTemplate();
                } else if ( parent instanceof TextDocumentDomainObject ) {
                    template = ((TextDocumentDomainObject)parent).getDefaultTemplate() ;
                }
                if ( null != template ) {
                    newTextDocument.setTemplate( template );
                }
            } else {
                newDocument = DocumentDomainObject.fromDocumentTypeId( documentTypeId );
                newDocument.setAttributes( (DocumentDomainObject.Attributes)parent.getAttributes().clone() );
            }
        } catch ( CloneNotSupportedException e ) {
            throw new UnhandledException( e );
        }
        newDocument.setId( 0 );
        newDocument.setCreator( user );
        newDocument.setStatus( DocumentDomainObject.STATUS_NEW );
        newDocument.setHeadline( "" );
        newDocument.setMenuText( "" );
        newDocument.setMenuImage( "" );
        newDocument.setPublicationStartDatetime( new Date() );
        newDocument.setArchivedDatetime( null );
        newDocument.setPublicationEndDatetime( null );
        return newDocument;
    }

    public CategoryDomainObject[] getAllCategoriesOfType( CategoryTypeDomainObject categoryType ) {
        String sqlQuery = "SELECT categories.category_id, categories.name, categories.description, categories.image\n"
                          + "FROM categories\n"
                          + "JOIN category_types ON categories.category_type_id = category_types.category_type_id\n"
                          + "WHERE categories.category_type_id = ?";
        String[][] sqlResult = service.sqlQueryMulti( sqlQuery, new String[]{"" + categoryType.getId()} );
        CategoryDomainObject[] categoryDomainObjects = new CategoryDomainObject[sqlResult.length];
        for ( int i = 0; i < sqlResult.length; i++ ) {
            int categoryId = Integer.parseInt( sqlResult[i][0] );
            String categoryName = sqlResult[i][1];
            String categoryDescription = sqlResult[i][2];
            String categoryImage = sqlResult[i][3];

            categoryDomainObjects[i] =
            new CategoryDomainObject( categoryId, categoryName, categoryDescription, categoryImage, categoryType );
        }
        return categoryDomainObjects;
    }

    public boolean isUniqueCategoryTypeName( String categoryTypeName ) {
        CategoryTypeDomainObject[] categoryTypes = getAllCategoryTypes();
        for ( int i = 0; i < categoryTypes.length; i++ ) {
            CategoryTypeDomainObject categoryType = categoryTypes[i];
            if ( categoryType.getName().equalsIgnoreCase( categoryTypeName ) ) {
                return false;
            }
        }
        return true;
    }

    public CategoryTypeDomainObject[] getAllCategoryTypes() {
        String sqlQuery = "SELECT category_type_id, name, max_choices FROM category_types ORDER BY name";
        String[][] sqlResult = service.sqlQueryMulti( sqlQuery, new String[0] );

        CategoryTypeDomainObject[] categoryTypeDomainObjects = new CategoryTypeDomainObject[sqlResult.length];
        for ( int i = 0; i < categoryTypeDomainObjects.length; i++ ) {
            int categoryTypeId = Integer.parseInt( sqlResult[i][0] );
            String typeName = sqlResult[i][1];
            int maxChoices = Integer.parseInt( sqlResult[i][2] );

            categoryTypeDomainObjects[i] = new CategoryTypeDomainObject( categoryTypeId, typeName, maxChoices );
        }

        return categoryTypeDomainObjects;
    }

    public SectionDomainObject[] getAllSections() {
        String[][] sqlRows = service.sqlProcedureMulti( SPROC_SECTION_GET_ALL, new String[0] );
        SectionDomainObject[] allSections = new SectionDomainObject[sqlRows.length];
        for ( int i = 0; i < sqlRows.length; i++ ) {
            int sectionId = Integer.parseInt( sqlRows[i][0] );
            String sectionName = sqlRows[i][1];
            allSections[i] = new SectionDomainObject( sectionId, sectionName );
        }
        Arrays.sort(allSections, new SectionNameComparator()) ;
        return allSections;
    }

    public CategoryDomainObject getCategory( CategoryTypeDomainObject categoryType, String categoryName ) {
        String sqlQuery = "SELECT categories.category_id, categories.name, categories.description, categories.image\n"
                          + "FROM categories\n"
                          + "JOIN category_types\n"
                          + "ON categories.category_type_id = category_types.category_type_id\n"
                          + "WHERE category_types.name = ?\n"
                          + "AND categories.name = ?";
        String[] sqlResult = service.sqlQuery( sqlQuery, new String[]{categoryType.getName(), categoryName} );
        if ( 0 != sqlResult.length ) {
            final int categoryId = Integer.parseInt( sqlResult[0] );
            final String categoryNameFromDb = sqlResult[1];
            final String categoryDescription = sqlResult[2];
            final String categoryImge = sqlResult[3];

            return new CategoryDomainObject( categoryId, categoryNameFromDb, categoryDescription, categoryImge,
                                             categoryType );
        } else {
            return null;
        }
    }

    public CategoryDomainObject getCategoryById( int categoryId ) {
        String sqlQuery = "SELECT categories.name, categories.description, categories.image, category_types.category_type_id, category_types.name, category_types.max_choices\n"
                          + "FROM categories\n"
                          + "JOIN category_types ON categories.category_type_id = category_types.category_type_id\n"
                          + "WHERE categories.category_id = ?";

        String[] categorySqlResult = service.sqlQuery( sqlQuery, new String[]{"" + categoryId} );

        if ( 0 != categorySqlResult.length ) {
            String categoryName = categorySqlResult[0];
            String categoryDescription = categorySqlResult[1];
            String categoryImage = categorySqlResult[2];
            int categoryTypeId = Integer.parseInt( categorySqlResult[3] );
            String categoryTypeName = categorySqlResult[4];
            int categoryTypeMaxChoices = Integer.parseInt( categorySqlResult[5] );

            CategoryTypeDomainObject categoryType = new CategoryTypeDomainObject( categoryTypeId, categoryTypeName,
                                                                                  categoryTypeMaxChoices );

            return new CategoryDomainObject( categoryId, categoryName, categoryDescription, categoryImage, categoryType );
        } else {
            return null;
        }
    }

    public CategoryTypeDomainObject getCategoryType( String categoryTypeName ) {
        String sqlStr = "SELECT category_types.category_type_id, category_types.name, category_types.max_choices\n"
                        + "FROM category_types\n"
                        + "WHERE category_types.name = ?";
        String[] sqlResult = service.sqlQuery( sqlStr, new String[]{categoryTypeName} );

        if ( null == sqlResult || 0 == sqlResult.length ) {
            return null;
        } else {
            int categoryTypeId = Integer.parseInt( sqlResult[0] );
            String categoryTypeNameFromDb = sqlResult[1];
            int categoryTypeMaxChoices = Integer.parseInt( sqlResult[2] );
            return new CategoryTypeDomainObject( categoryTypeId, categoryTypeNameFromDb, categoryTypeMaxChoices );
        }
    }

    public CategoryTypeDomainObject getCategoryTypeById( int categoryTypeId ) {
        String sqlStr = "select name, max_choices  from category_types where category_type_id = ? ";
        String[] sqlResult = service.sqlQuery( sqlStr, new String[]{"" + categoryTypeId} );

        if ( null == sqlResult || 0 == sqlResult.length ) {
            return null;
        } else {
            String categoryTypeNameFromDb = sqlResult[0];
            int categoryTypeMaxChoices = Integer.parseInt( sqlResult[1] );
            return new CategoryTypeDomainObject( categoryTypeId, categoryTypeNameFromDb, categoryTypeMaxChoices );
        }
    }

    public void deleteCategoryTypeFromDb( CategoryTypeDomainObject categoryType ) {
        String sqlstr = "delete from category_types where category_type_id = ?";
        service.sqlUpdateQuery( sqlstr, new String[]{categoryType.getId() + ""} );
    }

    public CategoryTypeDomainObject addCategoryTypeToDb( String name, int max_choices ) {
        String sqlstr = "insert into category_types (name, max_choices) values(?,?) SELECT @@IDENTITY";
        String newId = service.sqlQueryStr( sqlstr, new String[]{name, max_choices + ""} );
        return getCategoryTypeById( Integer.parseInt( newId ) );
    }

    public void updateCategoryType( CategoryTypeDomainObject categoryType ) {
        String sqlstr = "update category_types set name= ?, max_choices= ?  where category_type_id = ? ";
        service.sqlUpdateQuery( sqlstr, new String[]{
            categoryType.getName(), categoryType.getMaxChoices() + "", categoryType.getId() + ""
        } );
    }

    public CategoryDomainObject addCategoryToDb( int categoryTypeId, String name, String description, String image ) {
        String sqlstr = "insert into categories  (category_type_id, name, description, image) values(?,?,?,?) SELECT @@IDENTITY";
        String newId = service.sqlQueryStr( sqlstr, new String[]{
            categoryTypeId + "", name, description, image
        } );
        return getCategoryById( Integer.parseInt( newId ) );
    }

    public void updateCategory( CategoryDomainObject category ) {
        String sqlstr = "update categories set category_type_id = ?, name= ?, description = ?, image = ?  where category_id = ? ";
        service.sqlUpdateQuery( sqlstr, new String[]{
            category.getType().getId() + "", category.getName(), category.getDescription(), category.getImageUrl(),
            category.getId() + ""
        } );
    }

    public void deleteCategoryFromDb( CategoryDomainObject category ) {
        String sqlstr = "delete from categories where category_id = ?";
        service.sqlUpdateQuery( sqlstr, new String[]{category.getId() + ""} );
    }

    public DocumentDomainObject getDocument( int metaId ) {
        NDC.push( "getDocument" );

        DocumentDomainObject document ;
        try {
            document = (DocumentDomainObject)documentCache.get( new Integer( metaId ) );
            document = (DocumentDomainObject)document.clone();
        } catch ( CloneNotSupportedException e ) {
            throw new UnhandledException( e );
        }

        NDC.pop();
        return document;
    }

    public DocumentReference getDocumentReference( DocumentDomainObject document ) {
        return getDocumentReference( document.getId() ) ;
    }

    DocumentReference getDocumentReference( int childId ) {
        return new DocumentReference( childId, this );
    }

    private DocumentDomainObject getDocumentFromDb( int metaId ) {
        NDC.push( "getDocumentFromDb" );
        log.debug( "Getting document " + metaId + " from db." );

        String[] result = sprocGetDocumentInfo( metaId );

        DocumentDomainObject document = null;
        if ( 0 != result.length ) {
            document = getDocumentFromSqlResultRow( result );
            initDocumentAttributes( document ) ;
            initDocumentCategories( document );
            initRolesMappedToDocumentPermissionSetIds( document );

            document.accept( new DocumentInitializingVisitor() );
        }
        NDC.pop();
        return document;
    }

    public void initDocumentAttributes( DocumentDomainObject document ) {

        document.setSections( getSections( document.getId() ) );

        document.setKeywords( getKeywords( document.getId() ) );

        document.setPermissionSetForRestrictedOne( documentPermissionSetMapper.getPermissionSetRestrictedOne( document ) );
        document.setPermissionSetForRestrictedTwo( documentPermissionSetMapper.getPermissionSetRestrictedTwo( document ) );

        document.setPermissionSetForRestrictedOneForNewDocuments( documentPermissionSetMapper.getPermissionSetRestrictedOneForNewDocuments( document ) );
        document.setPermissionSetForRestrictedTwoForNewDocuments( documentPermissionSetMapper.getPermissionSetRestrictedTwoForNewDocuments( document ) );

    }

    public void initDocumentCategories( DocumentDomainObject document ) {

        addCategoriesFromDatabaseToDocument( document );

    }

    public void initRolesMappedToDocumentPermissionSetIds( DocumentDomainObject document ) {

        String[][] sprocResult = service.sqlQueryMulti( "SELECT "+ImcmsAuthenticatorAndUserAndRoleMapper.SQL_ROLES_COLUMNS+", rr.set_id\n"
                                                        + "FROM  roles, roles_rights AS rr\n"
                                                        + "WHERE rr.role_id = roles.role_id AND rr.meta_id = ?",
                                                        new String[]{"" + document.getId()} );

        ImcmsAuthenticatorAndUserAndRoleMapper imcmsAuthenticatorAndUserAndRoleMapper = service.getImcmsAuthenticatorAndUserAndRoleMapper();
        for ( int i = 0; i < sprocResult.length; ++i ) {
            RoleDomainObject role = imcmsAuthenticatorAndUserAndRoleMapper.getRoleFromSqlResult(sprocResult[i]) ;

            int rolePermissionSetId = Integer.parseInt( sprocResult[i][4] );
            document.setPermissionSetIdForRole( role, rolePermissionSetId );
        }

    }

    public SectionDomainObject getSectionById( int sectionId ) {
        String sectionName = service.sqlQueryStr( "SELECT section_name FROM sections WHERE section_id = ?",
                                                  new String[]{"" + sectionId} );
        if ( null == sectionName ) {
            return null;
        }
        return new SectionDomainObject( sectionId, sectionName );
    }

    public SectionDomainObject getSectionByName( String name ) {
        String[] sectionSqlRow = service.sqlQuery( "SELECT section_id, section_name FROM sections WHERE section_name = ?",
                                                   new String[]{name} );
        if ( 0 == sectionSqlRow.length ) {
            return null;
        }
        int sectionId = Integer.parseInt( sectionSqlRow[0] );
        String sectionName = sectionSqlRow[1];
        return new SectionDomainObject( sectionId, sectionName );
    }

    /**
     * @return the sections for a document, empty array if there is none.
     */
    private SectionDomainObject[] getSections( int meta_id ) {
        String[][] sectionData = service.sqlProcedureMulti( SPROC_SECTION_GET_INHERIT_ID,
                                                            new String[]{String.valueOf( meta_id )} );

        SectionDomainObject[] sections = new SectionDomainObject[sectionData.length];

        for ( int i = 0; i < sectionData.length; i++ ) {
            int sectionId = Integer.parseInt( sectionData[i][0] );
            String sectionName = sectionData[i][1];
            sections[i] = new SectionDomainObject( sectionId, sectionName );
        }
        return sections;
    }

    public TextDomainObject getText( int metaId, int no ) {
        String[] results = sprocGetText( metaId, no );

        if ( results == null || results.length == 0 ) {
            /* There was no text. Return null. */
            return null;
        }

        /* Return the text */
        String text = results[0];
        int type = Integer.parseInt( results[1] );

        return new TextDomainObject( text, type );

    }

    public void removeInclusion( int includingMetaId, int includeIndex ) {
        sprocDeleteInclude( service, includingMetaId, includeIndex );
    }

    public void saveNewDocument( DocumentDomainObject document, UserDomainObject user )
            throws MaxCategoryDomainObjectsOfTypeExceededException {

        if ( !user.canEdit( document ) ) {
            return; // TODO: More specific check needed. Throw exception ?
        }

        document.setCreatedDatetime( new Date() );
        
        int newMetaId = sqlInsertIntoMeta( document );

        if ( !user.isSuperAdminOrHasFullPermissionOn( document ) ) {
            document.setPermissionSetForRestrictedOne( document.getPermissionSetForRestrictedOneForNewDocuments() );
            document.setPermissionSetForRestrictedTwo( document.getPermissionSetForRestrictedTwoForNewDocuments() );
        }

        document.setId( newMetaId );
{'fraction_non_alphanumeric': 0.08583281674693624, 'fraction_numerical': 0.0030995183825282533, 'mean_word_length': 2.7606921904420334, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 38, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5391311', 'n_tokens_mistral': 10618, 'n_tokens_neox': 10280, 'n_words': 2364}