0001: /*
0002: Copyright (C) 2003 Know Gate S.L. All rights reserved.
0003: C/Oña, 107 1º2 28050 Madrid (Spain)
0004:
0005: Redistribution and use in source and binary forms, with or without
0006: modification, are permitted provided that the following conditions
0007: are met:
0008:
0009: 1. Redistributions of source code must retain the above copyright
0010: notice, this list of conditions and the following disclaimer.
0011:
0012: 2. The end-user documentation included with the redistribution,
0013: if any, must include the following acknowledgment:
0014: "This product includes software parts from hipergate
0015: (http://www.hipergate.org/)."
0016: Alternately, this acknowledgment may appear in the software itself,
0017: if and wherever such third-party acknowledgments normally appear.
0018:
0019: 3. The name hipergate must not be used to endorse or promote products
0020: derived from this software without prior written permission.
0021: Products derived from this software may not be called hipergate,
0022: nor may hipergate appear in their name, without prior written
0023: permission.
0024:
0025: This library is distributed in the hope that it will be useful,
0026: but WITHOUT ANY WARRANTY; without even the implied warranty of
0027: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
0028:
0029: You should have received a copy of hipergate License with this code;
0030: if not, visit http://www.hipergate.org or mail to info@hipergate.org
0031: */
0032:
0033: package com.knowgate.hipergate;
0034:
0035: import java.io.File;
0036: import java.io.IOException;
0037: import java.io.FileInputStream;
0038:
0039: import java.util.Properties;
0040:
0041: import java.util.LinkedList;
0042: import java.util.ListIterator;
0043: import java.util.NoSuchElementException;
0044: import java.util.StringTokenizer;
0045:
0046: import java.sql.Connection;
0047: import java.sql.ResultSet;
0048: import java.sql.Statement;
0049: import java.sql.PreparedStatement;
0050: import java.sql.CallableStatement;
0051: import java.sql.SQLException;
0052: import java.sql.DatabaseMetaData;
0053:
0054: import com.knowgate.debug.DebugFile;
0055: import com.knowgate.jdc.JDCConnection;
0056: import com.knowgate.dataobjs.DB;
0057: import com.knowgate.dataobjs.DBBind;
0058: import com.knowgate.dataobjs.DBPersist;
0059: import com.knowgate.dataobjs.DBSubset;
0060:
0061: import com.knowgate.misc.Gadgets;
0062: import com.knowgate.dfs.FileSystem;
0063:
0064: /**
0065: * Categories from k_categories database table
0066: * @author Sergio Montoro Ten
0067: * @version 2.2
0068: */
0069: public class Category extends DBPersist {
0070:
0071: /**
0072: * Create empty Category
0073: */
0074: public Category() {
0075: super (DB.k_categories, "Category");
0076: oFS = null;
0077: }
0078:
0079: // ----------------------------------------------------------
0080:
0081: /**
0082: * Create Category and set gu_category.
0083: * @param sIdCategory Category GUID
0084: * @throws SQLException
0085: */
0086: public Category(String sIdCategory) throws SQLException {
0087: super (DB.k_categories, "Category");
0088:
0089: put(DB.gu_category, sIdCategory);
0090: oFS = null;
0091: }
0092:
0093: // ----------------------------------------------------------
0094:
0095: /**
0096: * Load Category from database
0097: * @param oConn Database Connection
0098: * @param sIdCategory Category GUID
0099: * @throws SQLException
0100: */
0101: public Category(JDCConnection oConn, String sIdCategory)
0102: throws SQLException {
0103: super (DB.k_categories, "Category");
0104:
0105: Object aCatg[] = { sIdCategory };
0106:
0107: load(oConn, aCatg);
0108: oFS = null;
0109: }
0110:
0111: // ----------------------------------------------------------
0112:
0113: protected Category(String sTableName, String sClassName) {
0114: super (sTableName, sClassName);
0115: oFS = null;
0116: }
0117:
0118: // ==========================================================
0119:
0120: /**
0121: * <p>Get a list of all parents or childs of a Category.</p>
0122: * All levels up or down are scanned recursively.
0123: * @param oConn Database Connection
0124: * @param iDirection BROWSE_UP for browsing parents or BROWSE_DOWN for browsing childs.
0125: * @param iOrder BROWSE_TOPDOWN first element on the list will be the top most parent,
0126: * BROWSE_BOTTOMUP first element on the list will be the deepest child.
0127: * @return LinkedList of Category objects.
0128: * @throws SQLException
0129: */
0130: public LinkedList browse(JDCConnection oConn, int iDirection,
0131: int iOrder) throws SQLException {
0132: String sCatId = getString(DB.gu_category);
0133: String sNeighbour;
0134: boolean bDoNext;
0135: PreparedStatement oStmt;
0136: ResultSet oRSet;
0137: LinkedList oCatList = new LinkedList();
0138: Category oCatg;
0139:
0140: if (DebugFile.trace) {
0141: DebugFile
0142: .writeln("Begin Category.browse([Connection], ...)");
0143: DebugFile.incIdent();
0144: }
0145:
0146: if (iDirection == Category.BROWSE_UP) {
0147: if (DebugFile.trace)
0148: DebugFile.writeln("Connection.prepareStatement(SELECT "
0149: + DB.gu_parent_cat + " FROM " + DB.k_cat_tree
0150: + " WHERE " + DB.gu_child_cat + "=?)");
0151: oStmt = oConn.prepareStatement("SELECT " + DB.gu_parent_cat
0152: + " FROM " + DB.k_cat_tree + " WHERE "
0153: + DB.gu_child_cat + "=?",
0154: ResultSet.TYPE_FORWARD_ONLY,
0155: ResultSet.CONCUR_READ_ONLY);
0156: } else {
0157: if (DebugFile.trace)
0158: DebugFile.writeln("Connection.prepareStatement(SELECT "
0159: + DB.gu_child_cat + " FROM " + DB.k_cat_tree
0160: + " WHERE " + DB.gu_parent_cat + "=?)");
0161: oStmt = oConn.prepareStatement("SELECT " + DB.gu_child_cat
0162: + " FROM " + DB.k_cat_tree + " WHERE "
0163: + DB.gu_parent_cat + "=?",
0164: ResultSet.TYPE_FORWARD_ONLY,
0165: ResultSet.CONCUR_READ_ONLY);
0166: }
0167:
0168: do {
0169:
0170: if (DebugFile.trace)
0171: DebugFile.writeln("PreparedStatement.setString(1, "
0172: + sCatId + ")");
0173:
0174: oStmt.setString(1, sCatId);
0175: oRSet = oStmt.executeQuery();
0176: bDoNext = oRSet.next();
0177:
0178: if (bDoNext)
0179: sNeighbour = oRSet.getString(1);
0180: else
0181: sNeighbour = "";
0182:
0183: oRSet.close();
0184:
0185: if (bDoNext) {
0186: if (sCatId.equals(sNeighbour)) {
0187: bDoNext = false;
0188: } else {
0189: oCatg = new Category(oConn, sNeighbour);
0190:
0191: if (iDirection == Category.BROWSE_UP)
0192: if (iOrder == Category.BROWSE_BOTTOMUP)
0193: oCatList.addLast(oCatg);
0194: else
0195: oCatList.addFirst(oCatg);
0196: else if (iOrder == Category.BROWSE_BOTTOMUP)
0197: oCatList.addFirst(oCatg);
0198: else
0199: oCatList.addLast(oCatg);
0200:
0201: sCatId = sNeighbour;
0202: } // fi(sCatId==sNeighbour)
0203: } // fi (bDoNext)
0204: } while (bDoNext);
0205:
0206: oStmt.close();
0207:
0208: if (DebugFile.trace) {
0209: DebugFile.decIdent();
0210: DebugFile.writeln("End Category.browse() : "
0211: + String.valueOf(oCatList.size()));
0212: }
0213:
0214: return oCatList;
0215: } // browse
0216:
0217: // ----------------------------------------------------------
0218:
0219: /**
0220: * <p>Compose a path to Category by concatenating all parents names.</p>
0221: * Calls k_sp_get_cat_path.<br>
0222: * Category parents are found and each parent name is extracted.<br>
0223: * Then parent names are contenated in order separated by slash '/' characters.<br>
0224: * This method is usefull when creating a physical directory path for files
0225: * belonging to Products contained in a Category. This way the directory paths can
0226: * mimmic the category tree structure.
0227: * @param oConn Database Connection
0228: * @return String with Category parent names concatenated with slash '/' characters.
0229: * For example "ROOT/DOMAINS/SYSTEM/SYSTEM_APPS/SYSTEM_apps_webbuilder"
0230: * @throws SQLException
0231: */
0232: public String getPath(Connection oConn) throws SQLException {
0233: Statement oStmt;
0234: ResultSet oRSet;
0235: CallableStatement oCall;
0236: DatabaseMetaData oMDat;
0237: String sPath;
0238: String sDBMS;
0239:
0240: if (DebugFile.trace) {
0241: DebugFile.writeln("Begin Category.getPath([Connection])");
0242: DebugFile.incIdent();
0243: DebugFile.writeln("gu_category=" + get(DB.gu_category));
0244: }
0245:
0246: try {
0247: oMDat = oConn.getMetaData();
0248: if (null == oMDat)
0249: sDBMS = "unknown";
0250: else
0251: sDBMS = oConn.getMetaData().getDatabaseProductName();
0252: } catch (NullPointerException npe) {
0253: sDBMS = "unknown";
0254: }
0255:
0256: if (sDBMS.equals("PostgreSQL")) {
0257: oStmt = oConn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
0258: ResultSet.CONCUR_READ_ONLY);
0259:
0260: if (DebugFile.trace)
0261: DebugFile
0262: .writeln("Statement.executeQuery(SELECT k_sp_get_cat_path('"
0263: + getStringNull(DB.gu_category, "null")
0264: + "'))");
0265:
0266: oRSet = oStmt.executeQuery("SELECT k_sp_get_cat_path ('"
0267: + getString(DB.gu_category) + "')");
0268: oRSet.next();
0269: sPath = oRSet.getString(1);
0270: oRSet.close();
0271: oStmt.close();
0272: } else {
0273: if (DebugFile.trace)
0274: DebugFile.writeln("{call k_sp_get_cat_path ('"
0275: + getStringNull(DB.gu_category, "null")
0276: + "',?)}");
0277:
0278: oCall = oConn.prepareCall("{call k_sp_get_cat_path (?,?)}");
0279: oCall.setString(1, getString(DB.gu_category));
0280: oCall.registerOutParameter(2, java.sql.Types.VARCHAR);
0281: oCall.execute();
0282: sPath = oCall.getString(2);
0283: oCall.close();
0284: }
0285: // End SQLException
0286:
0287: if (DebugFile.trace) {
0288: DebugFile.decIdent();
0289: DebugFile.writeln("End Category.getPath() : " + sPath);
0290: }
0291:
0292: return sPath;
0293: } // getPath()
0294:
0295: // ----------------------------------------------------------
0296:
0297: /**
0298: * <p>Delete Category and all its childs.</p>
0299: * First delete all Products and Companies contained in Category, including
0300: * physical disk files associted with Products and Company attachments.<br>
0301: * Then call k_sp_del_category_r stored procedure and perform recursive
0302: * deletion of all childs.
0303: * @param oConn Database Connection
0304: * @throws SQLException
0305: */
0306: public boolean delete(JDCConnection oConn) throws SQLException {
0307: try {
0308: return Category.delete(oConn, getString(DB.gu_category));
0309: } catch (IOException ioe) {
0310: throw new SQLException("IOException " + ioe.getMessage());
0311: }
0312: } // delete
0313:
0314: // ----------------------------------------------------------
0315:
0316: /**
0317: * <p>Add object to Category.</p>
0318: * The object GUID and numeric class identifier is inserted at k_x_cat_objs table.<br>
0319: * @param oConn Database Connection
0320: * @param sIdObject Object GUID
0321: * @param iIdClass Object Numeric Class Identifier (variable ClassId)
0322: * @param iAttribs Object attributes mask (user defined)
0323: * @param iOdPosition Object Position. An arbitrary position for the object inside the
0324: * category. Position is not unique for an object. Two or more objects may have the same
0325: * position.
0326: * @throws SQLException If object is alredy contanied in Category then a primary key violation exception is raised.
0327: */
0328: public int addObject(Connection oConn, String sIdObject,
0329: int iIdClass, int iAttribs, int iOdPosition)
0330: throws SQLException {
0331: PreparedStatement oStmt;
0332: int iRetVal;
0333:
0334: if (DebugFile.trace) {
0335: DebugFile.writeln("Begin Category.addObject([Connection], "
0336: + sIdObject + ", ...)");
0337: DebugFile.incIdent();
0338: }
0339:
0340: oStmt = oConn.prepareStatement("INSERT INTO " + DB.k_x_cat_objs
0341: + " (" + DB.gu_category + "," + DB.gu_object + ","
0342: + DB.id_class + "," + DB.bi_attribs + ","
0343: + DB.od_position + ") VALUES (?,?,?,?,?)");
0344: oStmt.setString(1, getString(DB.gu_category));
0345: oStmt.setString(2, sIdObject);
0346: oStmt.setInt(3, iIdClass);
0347: oStmt.setInt(4, iAttribs);
0348: oStmt.setInt(5, iOdPosition);
0349: iRetVal = oStmt.executeUpdate();
0350: oStmt.close();
0351:
0352: if (DebugFile.trace) {
0353: DebugFile.decIdent();
0354: DebugFile.writeln("End Category.addProduct() : " + iRetVal);
0355: }
0356:
0357: return iRetVal;
0358: } // addProduct
0359:
0360: // ----------------------------------------------------------
0361:
0362: /**
0363: * <p>Remove object from Category</p>
0364: * Removing an object from a Category does not delete it.
0365: * @param oConn Database Connection
0366: * @param sIdObject Object GUID
0367: * @return 1 if object was present at category, 0 if object was not present at category.
0368: * @throws SQLException
0369: */
0370: public int removeObject(Connection oConn, String sIdObject)
0371: throws SQLException {
0372: int iRetVal;
0373: PreparedStatement oStmt = oConn.prepareStatement("DELETE FROM "
0374: + DB.k_x_cat_objs + " WHERE " + DB.gu_category
0375: + "=? AND " + DB.gu_object + "=?");
0376: oStmt.setString(1, getString(DB.gu_category));
0377: oStmt.setString(2, sIdObject);
0378: iRetVal = oStmt.executeUpdate();
0379: oStmt.close();
0380: return iRetVal;
0381: } // removeObject
0382:
0383: // ----------------------------------------------------------
0384:
0385: /**
0386: * <p>Set group permissions.</p>
0387: * Calls k_sp_cat_del_grp stored procedure.
0388: * @param oConn Database Connection
0389: * @param sIdGroups String of comma separated GUIDs of ACLGroups with permissions to remove.
0390: * @param iRecurse Remove permissions also from childs Categories all levels down.
0391: * @param iObjects Not Used, must be zero.
0392: * @throws SQLException
0393: */
0394: public void removeGroupPermissions(Connection oConn,
0395: String sIdGroups, short iRecurse, short iObjects)
0396: throws SQLException {
0397: CallableStatement oStmt;
0398: StringTokenizer oUsrTok;
0399: int iTokCount;
0400: String sIdCategory;
0401:
0402: if (DebugFile.trace) {
0403: DebugFile
0404: .writeln("Begin Category.removeGroupPermissions([Connection], "
0405: + sIdGroups
0406: + ","
0407: + iRecurse
0408: + ","
0409: + iObjects + ")");
0410: DebugFile.incIdent();
0411: DebugFile
0412: .writeln("Connection.prepareCall({ call k_sp_cat_del_grp ('"
0413: + getStringNull(DB.gu_category, "null")
0414: + "',?,"
0415: + String.valueOf(iRecurse)
0416: + ","
0417: + String.valueOf(iObjects) + ") }");
0418: }
0419:
0420: if (oConn.getMetaData().getDatabaseProductName().equals(
0421: "PostgreSQL"))
0422: oStmt = oConn.prepareCall("{ call k_sp_cat_del_grp ('"
0423: + getString(DB.gu_category) + "',?,CAST("
0424: + String.valueOf(iRecurse) + " AS SMALLINT), CAST("
0425: + String.valueOf(iObjects) + " AS SMALLINT)) }");
0426: else
0427: oStmt = oConn.prepareCall("{ call k_sp_cat_del_grp ('"
0428: + getString(DB.gu_category) + "',?,"
0429: + String.valueOf(iRecurse) + ","
0430: + String.valueOf(iObjects) + ") }");
0431:
0432: if (sIdGroups.indexOf(',') >= 0) {
0433: oUsrTok = new StringTokenizer(sIdGroups, ",");
0434: iTokCount = oUsrTok.countTokens();
0435: sIdCategory = getString(DB.gu_category);
0436:
0437: for (int t = 0; t < iTokCount; t++) {
0438: oStmt.setString(1, oUsrTok.nextToken());
0439: oStmt.execute();
0440: } // end for ()
0441:
0442: oStmt.close();
0443: } else {
0444: oStmt.setString(1, sIdGroups);
0445: oStmt.execute();
0446: oStmt.close();
0447: }
0448:
0449: if (DebugFile.trace) {
0450: DebugFile.decIdent();
0451: DebugFile
0452: .writeln("End Category.Category.removeGroupPermissions()");
0453: }
0454: } // removeGroupPermissions
0455:
0456: // ----------------------------------------------------------
0457:
0458: /**
0459: * <p>Set group permissions for Category</p>
0460: * Calls k_sp_cat_set_grp stored procedure.
0461: * @param oConn Database Connection
0462: * @param sIdGroups String of comma separated GUIDs of ACLGroups with permissions to set.
0463: * @param iACLMask Permissions mask, any combination of { ACL.PERMISSION_LIST,
0464: * ACL.PERMISSION_READ,ACL.PERMISSION_ADD,ACL.PERMISSION_DELETE,ACL.PERMISSION_MODIFY,
0465: * ACL.PERMISSION_MODERATE,ACL.PERMISSION_SEND,ACL.PERMISSION_GRANT,
0466: * ACL.PERMISSION_FULL_CONTROL }
0467: * @param iRecurse Remove permissions also from childs Categories all levels down.
0468: * @param iObjects Not Used, must be zero.
0469: * @throws SQLException
0470: * @see com.knowgate.acl.ACL
0471: */
0472: public void setGroupPermissions(Connection oConn, String sIdGroups,
0473: int iACLMask, short iRecurse, short iObjects)
0474: throws SQLException {
0475: PreparedStatement oStmt;
0476: CallableStatement oCall;
0477: StringTokenizer oUsrTok;
0478: String sToken;
0479: int iTokCount;
0480:
0481: if (DebugFile.trace) {
0482: DebugFile
0483: .writeln("Begin Category.setGroupPermissions([Connection], "
0484: + sIdGroups
0485: + ","
0486: + iACLMask
0487: + ","
0488: + iRecurse + "," + iObjects + ")");
0489: DebugFile.incIdent();
0490:
0491: DebugFile.writeln("database product name "
0492: + oConn.getMetaData().getDatabaseProductName());
0493:
0494: if (oConn.getMetaData().getDatabaseProductName().equals(
0495: "PostgreSQL"))
0496: DebugFile
0497: .writeln("Connection.prepareStatement(SELECT k_sp_cat_set_grp ('"
0498: + getString(DB.gu_category)
0499: + "',?,"
0500: + String.valueOf(iACLMask)
0501: + ", CAST("
0502: + String.valueOf(iRecurse)
0503: + " AS SMALLINT), CAST("
0504: + String.valueOf(iObjects)
0505: + " AS SMALLINT))");
0506: else
0507: DebugFile
0508: .writeln("Connection.prepareCall({ call k_sp_cat_set_grp ('"
0509: + getStringNull(DB.gu_category, "null")
0510: + "',?,"
0511: + String.valueOf(iACLMask)
0512: + ","
0513: + String.valueOf(iRecurse)
0514: + ","
0515: + String.valueOf(iObjects) + ") }");
0516: }
0517:
0518: if (oConn.getMetaData().getDatabaseProductName().equals(
0519: "PostgreSQL")) {
0520: oStmt = oConn.prepareStatement("SELECT k_sp_cat_set_grp ('"
0521: + getString(DB.gu_category) + "',?,"
0522: + String.valueOf(iACLMask) + ", CAST("
0523: + String.valueOf(iRecurse) + " AS SMALLINT), CAST("
0524: + String.valueOf(iObjects) + " AS SMALLINT))");
0525: if (sIdGroups.indexOf(',') > 0) {
0526: oUsrTok = new StringTokenizer(sIdGroups, ",");
0527: iTokCount = oUsrTok.countTokens();
0528: for (int t = 0; t < iTokCount; t++) {
0529: oStmt.setString(1, oUsrTok.nextToken());
0530: oStmt.execute();
0531: } // end for ()
0532: } else {
0533: oStmt.setString(1, sIdGroups);
0534: oStmt.execute();
0535: }
0536: oStmt.close();
0537: } else {
0538: oCall = oConn.prepareCall("{ call k_sp_cat_set_grp ('"
0539: + getString(DB.gu_category) + "',?,"
0540: + String.valueOf(iACLMask) + ","
0541: + String.valueOf(iRecurse) + ","
0542: + String.valueOf(iObjects) + ") }");
0543: if (sIdGroups.indexOf(',') > 0) {
0544: oUsrTok = new StringTokenizer(sIdGroups, ",");
0545: iTokCount = oUsrTok.countTokens();
0546: for (int t = 0; t < iTokCount; t++) {
0547: sToken = oUsrTok.nextToken();
0548: if (DebugFile.trace)
0549: DebugFile
0550: .writeln("CallableStatement.setString(1,"
0551: + sToken + ")");
0552: oCall.setString(1, sToken);
0553: oCall.execute();
0554: } // end for ()
0555: } else {
0556: if (DebugFile.trace)
0557: DebugFile.writeln("CallableStatement.setString(1,"
0558: + sIdGroups + ")");
0559: oCall.setString(1, sIdGroups);
0560: oCall.execute();
0561: }
0562: oCall.close();
0563: }
0564:
0565: if (DebugFile.trace) {
0566: String[] aGrps = com.knowgate.misc.Gadgets.split(sIdGroups,
0567: ',');
0568: int iMsk;
0569: for (int g = 0; g < aGrps.length; g++) {
0570: iMsk = getGroupPermissions(oConn, aGrps[g]);
0571: if (iMsk != iACLMask)
0572: throw new SQLException(
0573: "Procedure k_sp_cat_grp_perm returned a different permissions mask ("
0574: + String.valueOf(iMsk)
0575: + ") for group "
0576: + aGrps[g]
0577: + " on category "
0578: + getStringNull(DB.gu_category,
0579: null)
0580: + " than that set by k_sp_cat_set_grp ("
0581: + String.valueOf(iACLMask) + ")");
0582: }
0583: DebugFile.decIdent();
0584: DebugFile
0585: .writeln("End Category.Category.setGroupPermissions()");
0586: }
0587: } // setGroupPermissions
0588:
0589: // ----------------------------------------------------------
0590:
0591: /**
0592: * <p>Get User permissions for Category</p>
0593: * Calls k_sp_cat_usr_perm stored procedure.<br>
0594: * User permissions are those granted directy to user plus those grants
0595: * indirectly by assigning permisssion to a group witch the user belongs to.<br>
0596: * Permissions are accumulative; a user gains new permissions by belonging to
0597: * new groups. All permissions are of grant type, there are no deny permissions.
0598: * @param oConn Database Connection
0599: * @param sIdUser User GUID
0600: * @return User permissions mask. Any combination of:
0601: * { ACL.PERMISSION_LIST, ACL.PERMISSION_READ,ACL.PERMISSION_ADD,
0602: * ACL.PERMISSION_DELETE,ACL.PERMISSION_MODIFY, ACL.PERMISSION_MODERATE,
0603: * ACL.PERMISSION_SEND,ACL.PERMISSION_GRANT,ACL.PERMISSION_FULL_CONTROL }
0604: * @throws SQLException
0605: */
0606: public int getUserPermissions(Connection oConn, String sIdUser)
0607: throws SQLException {
0608: int iACLMask;
0609: CallableStatement oCall;
0610: Statement oStmt;
0611: ResultSet oRSet;
0612:
0613: if (DebugFile.trace) {
0614: DebugFile
0615: .writeln("Begin Category.getUserPermissions([Connection], "
0616: + sIdUser + ")");
0617: DebugFile.incIdent();
0618: }
0619:
0620: if (oConn.getMetaData().getDatabaseProductName().equals(
0621: "PostgreSQL")) {
0622: oStmt = oConn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
0623: ResultSet.CONCUR_READ_ONLY);
0624:
0625: if (DebugFile.trace)
0626: DebugFile
0627: .writeln("Statement.executeQuery(SELECT k_sp_cat_usr_perm ('"
0628: + sIdUser
0629: + "','"
0630: + getStringNull(DB.gu_category, "null")
0631: + "'))");
0632:
0633: oRSet = oStmt.executeQuery("SELECT k_sp_cat_usr_perm ('"
0634: + sIdUser + "','" + getString(DB.gu_category)
0635: + "')");
0636: oRSet.next();
0637: iACLMask = oRSet.getInt(1);
0638: oRSet.close();
0639: oStmt.close();
0640: } else {
0641: if (DebugFile.trace)
0642: DebugFile
0643: .writeln("Connection.prepareCall({ call k_sp_cat_usr_perm('"
0644: + sIdUser
0645: + "','"
0646: + getStringNull(DB.gu_category, null)
0647: + "',?) })");
0648:
0649: oCall = oConn
0650: .prepareCall("{ call k_sp_cat_usr_perm(?,?,?) }");
0651: oCall.setString(1, sIdUser);
0652: oCall.setString(2, getString(DB.gu_category));
0653: oCall.registerOutParameter(3, java.sql.Types.INTEGER);
0654: oCall.execute();
0655: iACLMask = oCall.getInt(3);
0656: oCall.close();
0657: }
0658:
0659: if (DebugFile.trace) {
0660: DebugFile.decIdent();
0661: DebugFile.writeln("End Category.getUserPermissions() : "
0662: + String.valueOf(iACLMask));
0663: }
0664:
0665: return iACLMask;
0666: } // getUserPermissions()
0667:
0668: // ----------------------------------------------------------
0669:
0670: /**
0671: * <p>Get permissions mas of a group over this category</p>
0672: * If there is no explicit permissions mask set at k_x_cat_group_acl for given
0673: * group and this category, then the category hierarchy is scanned upwards and
0674: * the permissions of the closest parent are assumed to be the ones of this category.
0675: * If no parent has explicit permissions set for given group then return value is zero.
0676: * @param oConn Database Connection
0677: * @param sIdGroup ACLGroup GUID
0678: * @return Group permissions mask. Any combination of:
0679: * { ACL.PERMISSION_LIST, ACL.PERMISSION_READ,ACL.PERMISSION_ADD,
0680: * ACL.PERMISSION_DELETE,ACL.PERMISSION_MODIFY, ACL.PERMISSION_MODERATE,
0681: * ACL.PERMISSION_SEND,ACL.PERMISSION_GRANT,ACL.PERMISSION_FULL_CONTROL }
0682: * @throws SQLException
0683: * @since 3.0
0684: */
0685: public int getGroupPermissions(Connection oConn, String sIdGroup)
0686: throws SQLException {
0687: int iACLMask;
0688: CallableStatement oCall;
0689: Statement oStmt;
0690: ResultSet oRSet;
0691:
0692: if (DebugFile.trace) {
0693: DebugFile
0694: .writeln("Begin Category.getGroupPermissions([Connection], "
0695: + sIdGroup + ")");
0696: DebugFile.incIdent();
0697: }
0698:
0699: if (oConn.getMetaData().getDatabaseProductName().equals(
0700: "PostgreSQL")) {
0701: oStmt = oConn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
0702: ResultSet.CONCUR_READ_ONLY);
0703:
0704: if (DebugFile.trace)
0705: DebugFile
0706: .writeln("Statement.executeQuery(SELECT k_sp_cat_grp_perm ('"
0707: + sIdGroup
0708: + "','"
0709: + getStringNull(DB.gu_category, "null")
0710: + "'))");
0711:
0712: oRSet = oStmt.executeQuery("SELECT k_sp_cat_grp_perm ('"
0713: + sIdGroup + "','" + getString(DB.gu_category)
0714: + "')");
0715: oRSet.next();
0716: iACLMask = oRSet.getInt(1);
0717: oRSet.close();
0718: oStmt.close();
0719: } else {
0720: if (DebugFile.trace)
0721: DebugFile
0722: .writeln("Connection.prepareCall({ call k_sp_cat_grp_perm('"
0723: + sIdGroup
0724: + "','"
0725: + getStringNull(DB.gu_category, null)
0726: + "',?) })");
0727:
0728: oCall = oConn
0729: .prepareCall("{ call k_sp_cat_grp_perm(?,?,?) }");
0730: oCall.setString(1, sIdGroup);
0731: oCall.setString(2, getString(DB.gu_category));
0732: oCall.registerOutParameter(3, java.sql.Types.INTEGER);
0733: oCall.execute();
0734: iACLMask = oCall.getInt(3);
0735: oCall.close();
0736: }
0737:
0738: if (DebugFile.trace) {
0739: DebugFile.decIdent();
0740: DebugFile.writeln("End Category.getGroupPermissions() : "
0741: + String.valueOf(iACLMask));
0742: }
0743:
0744: return iACLMask;
0745: } // getGroupPermissions()
0746:
0747: // ----------------------------------------------------------
0748:
0749: /**
0750: * <p>Remove permissions for user at a Category.</p>
0751: * Calls k_sp_cat_del_usr.<br>
0752: * Only permissions directly granted to user are removed.<br>
0753: * Permissions obtained by belonging to a Group remain active.<br>
0754: * @param oConn Database Connection
0755: * @param sIdUsers String of user GUIDs separated by commas.
0756: * @param iRecurse Remove permissions from child categories.
0757: * @param iObjects Not used, must be zero.
0758: * @throws SQLException
0759: */
0760: public void removeUserPermissions(Connection oConn,
0761: String sIdUsers, short iRecurse, short iObjects)
0762: throws SQLException {
0763: CallableStatement oStmt;
0764: StringTokenizer oUsrTok;
0765: int iTokCount;
0766:
0767: if (DebugFile.trace) {
0768: DebugFile
0769: .writeln("Begin Category.removeUserPermissions([Connection], "
0770: + sIdUsers
0771: + ","
0772: + iRecurse
0773: + ","
0774: + iObjects + ")");
0775: DebugFile.incIdent();
0776: DebugFile
0777: .writeln("Connection.prepareCall({ call k_sp_cat_del_usr ('"
0778: + getStringNull(DB.gu_category, "null")
0779: + "',?,"
0780: + String.valueOf(iRecurse)
0781: + ","
0782: + String.valueOf(iObjects) + ") }");
0783: }
0784:
0785: if (oConn.getMetaData().getDatabaseProductName().equals(
0786: "PostgreSQL"))
0787: oStmt = oConn.prepareCall("{ call k_sp_cat_del_usr ('"
0788: + getString(DB.gu_category) + "',?, CAST("
0789: + String.valueOf(iRecurse) + " AS SMALLINT), CAST("
0790: + String.valueOf(iObjects) + " AS SMALLINT)) }");
0791: else
0792: oStmt = oConn.prepareCall("{ call k_sp_cat_del_usr ('"
0793: + getString(DB.gu_category) + "',?,"
0794: + String.valueOf(iRecurse) + ","
0795: + String.valueOf(iObjects) + ") }");
0796:
0797: if (sIdUsers.indexOf(',') >= 0) {
0798: oUsrTok = new StringTokenizer(sIdUsers, ",");
0799: iTokCount = oUsrTok.countTokens();
0800:
0801: for (int t = 0; t < iTokCount; t++) {
0802: oStmt.setString(1, oUsrTok.nextToken());
0803: oStmt.execute();
0804: } // end for ()
0805:
0806: oStmt.close();
0807: } else {
0808: oStmt.setString(1, sIdUsers);
0809: oStmt.execute();
0810: oStmt.close();
0811: }
0812:
0813: if (DebugFile.trace) {
0814: DebugFile.decIdent();
0815: DebugFile
0816: .writeln("End Category.Category.removeUserPermissions()");
0817: }
0818: } // removeUserPermissions
0819:
0820: // ----------------------------------------------------------
0821:
0822: /**
0823: * <p>Set user permissions for a Category.</p>
0824: * Calls k_sp_cat_set_usr stored procedure.
0825: * @param oConn Database Connection
0826: * @param sIdUsers String of user GUIDs separated by commas.
0827: * @param iACLMask Permissions mask. Any combination of:
0828: * { ACL.PERMISSION_LIST, ACL.PERMISSION_READ,ACL.PERMISSION_ADD,
0829: * ACL.PERMISSION_DELETE,ACL.PERMISSION_MODIFY, ACL.PERMISSION_MODERATE,
0830: * ACL.PERMISSION_SEND,ACL.PERMISSION_GRANT,ACL.PERMISSION_FULL_CONTROL }
0831: * @param iRecurse Remove permissions from child categories.
0832: * @param iObjects Not used, must be zero.
0833: * @throws SQLException
0834: */
0835: public void setUserPermissions(Connection oConn, String sIdUsers,
0836: int iACLMask, short iRecurse, short iObjects)
0837: throws SQLException {
0838: CallableStatement oStmt;
0839: StringTokenizer oUsrTok;
0840: String sSQL;
0841: String sUserId;
0842: int iTokCount;
0843:
0844: if (DebugFile.trace) {
0845: DebugFile
0846: .writeln("Begin Category.setUserPermissions([Connection], "
0847: + sIdUsers
0848: + ","
0849: + iACLMask
0850: + ","
0851: + iRecurse + "," + iObjects + ")");
0852: DebugFile.incIdent();
0853: DebugFile.writeln(" " + DB.gu_category + "="
0854: + getStringNull(DB.gu_category, "null"));
0855: }
0856:
0857: if (oConn.getMetaData().getDatabaseProductName().equals(
0858: "PostgreSQL"))
0859: sSQL = "{ call k_sp_cat_set_usr (?,?,"
0860: + String.valueOf(iACLMask) + ", CAST("
0861: + String.valueOf(iRecurse) + " AS SMALLINT), CAST("
0862: + String.valueOf(iObjects) + " AS SMALLINT)) }";
0863: else
0864: sSQL = "{ call k_sp_cat_set_usr (?,?,"
0865: + String.valueOf(iACLMask) + ","
0866: + String.valueOf(iRecurse) + ","
0867: + String.valueOf(iObjects) + ") }";
0868:
0869: if (DebugFile.trace)
0870: DebugFile.writeln("Connection.prepareCall(" + sSQL + ")");
0871:
0872: oStmt = oConn.prepareCall(sSQL);
0873:
0874: if (sIdUsers.indexOf(',') > 0) {
0875: oUsrTok = new StringTokenizer(sIdUsers, ",");
0876: iTokCount = oUsrTok.countTokens();
0877:
0878: for (int t = 0; t < iTokCount; t++) {
0879: sUserId = oUsrTok.nextToken();
0880:
0881: if (DebugFile.trace)
0882: DebugFile.writeln("binding user "
0883: + String.valueOf(t + 1) + " " + sUserId);
0884:
0885: oStmt.setObject(1, getString(DB.gu_category),
0886: java.sql.Types.CHAR);
0887: oStmt.setObject(2, sUserId, java.sql.Types.CHAR);
0888: oStmt.execute();
0889: } // end for ()
0890:
0891: } else {
0892: if (DebugFile.trace)
0893: DebugFile.writeln("binding user " + sIdUsers);
0894:
0895: oStmt.setObject(1, getString(DB.gu_category),
0896: java.sql.Types.CHAR);
0897: oStmt.setObject(2, sIdUsers, java.sql.Types.CHAR);
0898: oStmt.execute();
0899: }
0900:
0901: oStmt.close();
0902:
0903: if (DebugFile.trace) {
0904: DebugFile.decIdent();
0905: DebugFile.writeln("End Category.setUserPermissions()");
0906: }
0907: } // setUserPermissions
0908:
0909: // ----------------------------------------------------------
0910:
0911: /**
0912: * <p>Inherits permissions from another Category.</p>
0913: * All previous permissions on this Category are removed before copying
0914: * permission from the other Category.
0915: * @param oConn Database Connection
0916: * @param sFromCategory GUID of category with permissions to be inherited.
0917: * @param iRecurse Propagate permissions to child categories.
0918: * @param iObjects Not used, must be zero.
0919: * @throws SQLException
0920: */
0921: public void inheritPermissions(JDCConnection oConn,
0922: String sFromCategory, short iRecurse, short iObjects)
0923: throws SQLException {
0924: int i;
0925: int iUsrPerms;
0926: int iGrpPerms;
0927: String sIdCategory = getString(DB.gu_category);
0928: DBSubset oUsrPerms = new DBSubset(DB.k_x_cat_user_acl,
0929: DB.gu_user + "," + DB.acl_mask, DB.gu_category + "='"
0930: + sFromCategory + "'", 100);
0931: DBSubset oGrpPerms = new DBSubset(DB.k_x_cat_group_acl,
0932: DB.gu_acl_group + "," + DB.acl_mask, DB.gu_category
0933: + "='" + sFromCategory + "'", 100);
0934: Statement oDelete = oConn.createStatement();
0935: PreparedStatement oInsert;
0936:
0937: if (DebugFile.trace) {
0938: DebugFile
0939: .writeln("Begin Category.inheritPermissions([Connection], "
0940: + sFromCategory
0941: + ","
0942: + iRecurse
0943: + ","
0944: + iObjects + ")");
0945: DebugFile.incIdent();
0946: }
0947:
0948: if (DebugFile.trace)
0949: DebugFile.writeln(" loading user permissions from "
0950: + DB.k_x_cat_user_acl);
0951:
0952: iUsrPerms = oUsrPerms.load(oConn);
0953:
0954: if (DebugFile.trace)
0955: DebugFile.writeln(" loading group permissions from "
0956: + DB.k_x_cat_group_acl);
0957:
0958: iGrpPerms = oGrpPerms.load(oConn);
0959:
0960: if (DebugFile.trace)
0961: DebugFile.writeln(" Connection.executeUpdate("
0962: + "DELETE FROM " + DB.k_x_cat_user_acl + " WHERE "
0963: + DB.gu_category + "='" + sIdCategory + "')");
0964:
0965: oDelete
0966: .executeUpdate("DELETE FROM " + DB.k_x_cat_user_acl
0967: + " WHERE " + DB.gu_category + "='"
0968: + sIdCategory + "'");
0969:
0970: if (DebugFile.trace)
0971: DebugFile.writeln(" Connection.executeUpdate("
0972: + "DELETE FROM " + DB.k_x_cat_group_acl + " WHERE "
0973: + DB.gu_category + "='" + sIdCategory + "')");
0974:
0975: oDelete
0976: .executeUpdate("DELETE FROM " + DB.k_x_cat_group_acl
0977: + " WHERE " + DB.gu_category + "='"
0978: + sIdCategory + "'");
0979:
0980: oDelete.close();
0981: oDelete = null;
0982:
0983: if (DebugFile.trace)
0984: DebugFile.writeln(" Connection.prepareStatement("
0985: + "INSERT INTO " + DB.k_x_cat_user_acl + "("
0986: + DB.gu_category + "," + DB.gu_user + ","
0987: + DB.acl_mask + ") VALUES (?,?,?))");
0988:
0989: oInsert = oConn.prepareStatement("INSERT INTO "
0990: + DB.k_x_cat_user_acl + "(" + DB.gu_category + ","
0991: + DB.gu_user + "," + DB.acl_mask + ") VALUES (?,?,?)");
0992:
0993: for (i = 0; i < iUsrPerms; i++) {
0994: oInsert.setString(1, sIdCategory);
0995: oInsert.setString(2, oUsrPerms.getString(0, i));
0996: oInsert.setInt(3, oUsrPerms.getInt(1, i));
0997:
0998: if (DebugFile.trace)
0999: DebugFile
1000: .writeln(" PreparedStatement.executeUpdate("
1001: + sIdCategory + ","
1002: + oUsrPerms.getString(0, i) + ","
1003: + oUsrPerms.getInt(1, i) + ")");
1004: oInsert.executeUpdate();
1005: oInsert.close();
1006: }
1007:
1008: if (DebugFile.trace)
1009: DebugFile.writeln(" Connection.prepareStatement("
1010: + "INSERT INTO " + DB.k_x_cat_group_acl + "("
1011: + DB.gu_category + "," + DB.gu_acl_group + ","
1012: + DB.acl_mask + ") VALUES (?,?,?))");
1013:
1014: oInsert = oConn.prepareStatement("INSERT INTO "
1015: + DB.k_x_cat_group_acl + "(" + DB.gu_category + ","
1016: + DB.gu_acl_group + "," + DB.acl_mask
1017: + ") VALUES (?,?,?)");
1018: for (i = 0; i < iGrpPerms; i++) {
1019: oInsert.setString(1, sIdCategory);
1020: oInsert.setString(2, oGrpPerms.getString(0, i));
1021: oInsert.setInt(3, oGrpPerms.getInt(1, i));
1022:
1023: if (DebugFile.trace)
1024: DebugFile
1025: .writeln(" PreparedStatement.executeUpdate("
1026: + sIdCategory + ","
1027: + oGrpPerms.getString(0, i) + ","
1028: + oGrpPerms.getInt(1, i) + ")");
1029: oInsert.executeUpdate();
1030: oInsert.close();
1031: }
1032:
1033: if (DebugFile.trace) {
1034: DebugFile.decIdent();
1035: DebugFile.writeln("End Category.inheritPermissions()");
1036: }
1037: } // inheritPermissions
1038:
1039: // ----------------------------------------------------------
1040:
1041: /**
1042: * Get whether or not this category descends at any level from another one.
1043: * @param oConn Database Connection
1044: * @param sParentCategory Parent category
1045: * @return <b>true</b> if this category descends at any level of depth from sParentCategory.
1046: * @throws SQLException
1047: */
1048: public boolean isChildOf(Connection oConn, String sParentCategory)
1049: throws SQLException {
1050: String sSelfId = getString(DB.gu_category);
1051: String sChild;
1052: boolean isChild = false;
1053: boolean bDoNext;
1054: PreparedStatement oStmt = oConn
1055: .prepareStatement("SELECT " + DB.gu_child_cat
1056: + " FROM " + DB.k_cat_tree + " WHERE "
1057: + DB.gu_parent_cat + "=?",
1058: ResultSet.TYPE_FORWARD_ONLY,
1059: ResultSet.CONCUR_READ_ONLY);
1060: ResultSet oRSet;
1061:
1062: do {
1063: oStmt.setString(1, sParentCategory);
1064: oRSet = oStmt.executeQuery();
1065: bDoNext = oRSet.next();
1066: if (bDoNext)
1067: sChild = oRSet.getString(1);
1068: else
1069: sChild = "-1";
1070: oRSet.close();
1071:
1072: if (bDoNext) {
1073: if (sChild.equals(sParentCategory)) {
1074: bDoNext = false;
1075: } else if (sChild.equals(sSelfId)) {
1076: isChild = true;
1077: bDoNext = false;
1078: } else {
1079: sParentCategory = sChild;
1080: }
1081: } // endif (bDoNext)
1082: } while (bDoNext);
1083:
1084: oStmt.close();
1085:
1086: return isChild;
1087: } // isChildOf
1088:
1089: // ----------------------------------------------------------
1090:
1091: /**
1092: * Get whether or not this category is parent at any level of another one.
1093: * @param oConn Database Connection
1094: * @param sChildCategory Child Category GUID
1095: * @return <b>true</b> if this category is parent at any level.
1096: * @throws SQLException
1097: */
1098: public boolean isParentOf(Connection oConn, String sChildCategory)
1099: throws SQLException {
1100: String sSelfId;
1101: String sParnt;
1102: boolean isParent = false;
1103: boolean bDoNext;
1104: PreparedStatement oStmt = oConn
1105: .prepareStatement("SELECT " + DB.gu_parent_cat
1106: + " FROM " + DB.k_cat_tree + " WHERE "
1107: + DB.gu_child_cat + "=?",
1108: ResultSet.TYPE_FORWARD_ONLY,
1109: ResultSet.CONCUR_READ_ONLY);
1110: ResultSet oRSet;
1111:
1112: if (DebugFile.trace) {
1113: DebugFile.writeln("Begin Category.isParentOf("
1114: + sChildCategory + ")");
1115: DebugFile.incIdent();
1116: }
1117:
1118: sSelfId = getString(DB.gu_category);
1119:
1120: if (DebugFile.trace)
1121: DebugFile.writeln(" " + DB.gu_category + " = " + sSelfId);
1122:
1123: do {
1124: oStmt.setString(1, sChildCategory);
1125: oRSet = oStmt.executeQuery();
1126: bDoNext = oRSet.next();
1127: if (bDoNext)
1128: sParnt = oRSet.getString(1);
1129: else
1130: sParnt = "-1";
1131: oRSet.close();
1132:
1133: if (DebugFile.trace)
1134: DebugFile.writeln(" id_parent = " + sParnt);
1135:
1136: if (bDoNext) {
1137: if (sParnt.equals(sChildCategory)) {
1138: bDoNext = false;
1139: } else if (sParnt.equals(sSelfId)) {
1140: isParent = true;
1141: bDoNext = false;
1142: } else {
1143: sChildCategory = sParnt;
1144: }
1145: } // endif (bDoNext)
1146: } while (bDoNext);
1147:
1148: oStmt.close();
1149:
1150: if (DebugFile.trace) {
1151: DebugFile.decIdent();
1152: DebugFile
1153: .writeln("End Category.isParentOf() : " + isParent);
1154: }
1155:
1156: return isParent;
1157: } // isParentOf
1158:
1159: // ----------------------------------------------------------
1160:
1161: /**
1162: * <p>Get category depth level.</p>
1163: * Calls k_sp_cat_level stored procedure.<br>
1164: * Root Categories have level 1.
1165: * @param oConn Database Connection
1166: * @return Category depth evel starting at 1.
1167: * @throws SQLException
1168: */
1169: public int level(JDCConnection oConn) throws SQLException {
1170: int iLevel;
1171: CallableStatement oCall;
1172: Statement oStmt;
1173: ResultSet oRSet;
1174:
1175: if (oConn.getDataBaseProduct() == JDCConnection.DBMS_POSTGRESQL) {
1176: oStmt = oConn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
1177: ResultSet.CONCUR_READ_ONLY);
1178: oRSet = oStmt.executeQuery("SELECT k_sp_cat_level('"
1179: + getString(DB.gu_category) + "')");
1180: oRSet.next();
1181: iLevel = oRSet.getInt(1);
1182: oRSet.close();
1183: oStmt.close();
1184: } else {
1185: oCall = oConn.prepareCall("{ call k_sp_cat_level(?,?)}");
1186: oCall.setString(1, getString(DB.gu_category));
1187: oCall.registerOutParameter(2, java.sql.Types.INTEGER);
1188: oCall.execute();
1189: iLevel = oCall.getInt(2);
1190: oCall.close();
1191: }
1192:
1193: return iLevel;
1194: } // level
1195:
1196: // ----------------------------------------------------------
1197:
1198: /**
1199: * @param oConn Database Connection
1200: * @return <b>true</b> if Category is present at k_cat_root table.
1201: * @throws SQLException
1202: */
1203: public boolean getIsRoot(Connection oConn) throws SQLException {
1204: Statement oStmt;
1205: ResultSet oRSet;
1206: boolean bRoot = false;
1207:
1208: // Begin SQLException
1209: oStmt = oConn.createStatement();
1210: oRSet = oStmt.executeQuery("SELECT " + DB.gu_category
1211: + " FROM " + DB.k_cat_root + " WHERE " + DB.gu_category
1212: + "='" + getString(DB.gu_category) + "'");
1213:
1214: bRoot = oRSet.next();
1215:
1216: oRSet.close();
1217: oStmt.close();
1218: // End SQLException
1219:
1220: return bRoot;
1221: } // getIsRoot
1222:
1223: // ----------------------------------------------------------
1224:
1225: /**
1226: * Make or unmake a root category.
1227: * @param oConn Database Connection
1228: * @param bIsRoot <b>true</b> if category is to be made root.
1229: * @throws SQLException If This Category is present as a child of another
1230: * category at k_cat_tree table.
1231: */
1232: public void setIsRoot(Connection oConn, boolean bIsRoot)
1233: throws SQLException {
1234: Statement oStmt;
1235: ResultSet oRSet;
1236: boolean bIsChild;
1237:
1238: if (DebugFile.trace) {
1239: DebugFile.writeln("Begin Category.setIsRoot([Connection], "
1240: + String.valueOf(bIsRoot) + ")");
1241: DebugFile.incIdent();
1242: }
1243:
1244: // Begin SQLException
1245: if (bIsRoot) {
1246: oStmt = oConn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
1247: ResultSet.CONCUR_READ_ONLY);
1248: if (DebugFile.trace)
1249: DebugFile
1250: .writeln("Statement.executeQuery(SELECT NULL FROM "
1251: + DB.k_cat_tree
1252: + " WHERE "
1253: + DB.gu_child_cat
1254: + "='"
1255: + getStringNull(DB.gu_category, "null")
1256: + "')");
1257: oRSet = oStmt.executeQuery("SELECT NULL FROM "
1258: + DB.k_cat_tree + " WHERE " + DB.gu_child_cat
1259: + "='" + getString(DB.gu_category) + "'");
1260: bIsChild = oRSet.next();
1261: oRSet.close();
1262: oStmt.close();
1263:
1264: if (bIsChild)
1265: throw new SQLException(
1266: "Category cannot be set Root if present as a child at k_cat_tree table");
1267: }
1268:
1269: oStmt = oConn.createStatement();
1270:
1271: if (DebugFile.trace)
1272: DebugFile.writeln("Statement.executeUpdate(DELETE FROM "
1273: + DB.k_cat_root + " WHERE " + DB.gu_category + "='"
1274: + getStringNull(DB.gu_category, "null") + "')");
1275:
1276: oStmt.executeUpdate("DELETE FROM " + DB.k_cat_root + " WHERE "
1277: + DB.gu_category + "='" + getString(DB.gu_category)
1278: + "'");
1279:
1280: if (bIsRoot) {
1281: if (DebugFile.trace)
1282: DebugFile
1283: .writeln("Statement.executeUpdate(INSERT INTO "
1284: + DB.k_cat_root + "(" + DB.gu_category
1285: + ") VALUES ('"
1286: + getStringNull(DB.gu_category, "null")
1287: + "')");
1288:
1289: oStmt.executeUpdate("INSERT INTO " + DB.k_cat_root + "("
1290: + DB.gu_category + ") VALUES ('"
1291: + getString(DB.gu_category) + "')");
1292: }
1293:
1294: oStmt.close();
1295: // End SQLException
1296:
1297: if (DebugFile.trace) {
1298: DebugFile.decIdent();
1299: DebugFile.writeln("End Category.setIsRoot()");
1300: }
1301: } // setIsRoot
1302:
1303: // ----------------------------------------------------------
1304: /**
1305: * Get translated label for a category.
1306: * @param oConn Database Connection
1307: * @param sLanguage Language code from k_lu_languages table.
1308: * @return Translated label or <b>null</b> if no translated label for such
1309: * language was found at k_cat_labels table.
1310: * @throws SQLException
1311: */
1312: public String getLabel(Connection oConn, String sLanguage)
1313: throws SQLException {
1314: String sTr;
1315:
1316: if (DebugFile.trace) {
1317: DebugFile.writeln("Begin Category.getLabel([Connection], "
1318: + sLanguage + ")");
1319: DebugFile.incIdent();
1320: DebugFile.writeln("Connection.prepareStatement(SELECT "
1321: + DB.tr_category + " FROM " + DB.k_cat_labels
1322: + " WHERE " + DB.gu_category + "='"
1323: + get(DB.gu_category) + "' AND " + DB.id_language
1324: + "='" + sLanguage + "'");
1325: }
1326:
1327: PreparedStatement oStmt = oConn.prepareStatement("SELECT "
1328: + DB.tr_category + " FROM " + DB.k_cat_labels
1329: + " WHERE " + DB.gu_category + "=? AND "
1330: + DB.id_language + "=?", ResultSet.TYPE_FORWARD_ONLY,
1331: ResultSet.CONCUR_READ_ONLY);
1332: oStmt.setString(1, getString(DB.gu_category));
1333: oStmt.setString(2, sLanguage);
1334:
1335: ResultSet oRSet = oStmt.executeQuery();
1336: if (oRSet.next())
1337: sTr = oRSet.getString(1);
1338: else
1339: sTr = null;
1340: oRSet.close();
1341: oStmt.close();
1342:
1343: if (DebugFile.trace) {
1344: DebugFile.decIdent();
1345: DebugFile.writeln("End Category.getLabel() : " + sTr);
1346: }
1347:
1348: return sTr;
1349: } // getLabel()
1350:
1351: // ----------------------------------------------------------
1352:
1353: /**
1354: * <p>Get Category translated labels as a DBSubset.</p>
1355: * @param oConn Database Connection
1356: * @return DBSubset with columns:<br>
1357: * <table border=1 cellpadding=4>
1358: * <tr><td><b>id_language</b></td><td><b>tr_category</b></td><td><b>url_category</b></td></tr>
1359: * <tr><td>2 chras. Lang. Id.</td><td>Translated Category Name</td><td>URL for Category</td></tr>
1360: * </table>
1361: * @throws SQLException
1362: */
1363: public DBSubset getNames(JDCConnection oConn) throws SQLException {
1364: Object aCatg[] = { get(DB.gu_category) };
1365:
1366: oNames = new DBSubset(DB.k_cat_labels, DB.id_language + ","
1367: + DB.tr_category + "," + DB.url_category,
1368: DB.gu_category + "=?", 4);
1369: oNames.load(oConn, aCatg);
1370:
1371: return oNames;
1372: } // getNames
1373:
1374: // ----------------------------------------------------------
1375:
1376: /**
1377: * <p>Get first level childs as a DBSubset.</p>
1378: * @param oConn Database Connection
1379: * @return Single column DBSubset with child GUIDs
1380: * @throws SQLException
1381: */
1382: public DBSubset getChilds(JDCConnection oConn) throws SQLException {
1383: Object aCatg[] = { get(DB.gu_category) };
1384:
1385: oChilds = new DBSubset(DB.k_cat_tree, DB.gu_child_cat,
1386: DB.gu_parent_cat + "=?", 1);
1387:
1388: oChilds.load(oConn, aCatg);
1389:
1390: return oChilds;
1391: } // getChilds
1392:
1393: // ----------------------------------------------------------
1394:
1395: /**
1396: * <p>Get inmediate parents as a DBSubset.</p>
1397: * @param oConn Database Connection
1398: * @return Single column DBSubset with parent GUIDs
1399: * @throws SQLException
1400: */
1401: public DBSubset getParents(JDCConnection oConn) throws SQLException {
1402: Object aCatg[] = { get(DB.gu_category) };
1403:
1404: oParents = new DBSubset(DB.k_cat_tree, DB.gu_parent_cat,
1405: DB.gu_child_cat + "=?", 1);
1406: oParents.load(oConn, aCatg);
1407:
1408: return oParents;
1409: } // getParents
1410:
1411: // ----------------------------------------------------------
1412:
1413: /**
1414: * Get objects contained at Category.
1415: * @param oConn Database Connection
1416: * @return DBSubset with columns:
1417: * <table border=1 cellpadding=4>
1418: * <tr><td><b>gu_object</b></td><td><b>id_class</b></td><td><b>bi_attribs</b></td></tr>
1419: * </table>
1420: * @throws SQLException
1421: */
1422: public DBSubset getObjects(JDCConnection oConn) throws SQLException {
1423: Object aCatg[] = { get(DB.gu_category) };
1424: DBSubset oObjs;
1425:
1426: oObjs = new DBSubset(DB.k_x_cat_objs, DB.gu_object + ","
1427: + DB.id_class + "," + DB.bi_attribs, DB.gu_category
1428: + "=? ORDER BY " + DB.od_position, 64);
1429:
1430: oObjs.load(oConn, new Object[] { getString(DB.gu_category) });
1431:
1432: return oObjs;
1433: } // getObjects
1434:
1435: // ----------------------------------------------------------
1436:
1437: /**
1438: * Get Groups with permissions over this Category.
1439: * @param oConn Database Connection
1440: * @return A DBSubset with 2 columns: gu_acl_group, acl_mask
1441: * @throws SQLException
1442: */
1443: public DBSubset getACLGroups(JDCConnection oConn)
1444: throws SQLException {
1445: Object aCatg[] = { get(DB.gu_category) };
1446:
1447: oACLGroups = new DBSubset(DB.k_x_cat_group_acl, DB.gu_acl_group
1448: + "," + DB.acl_mask, DB.gu_category + "=?", 50);
1449: oACLGroups.load(oConn, aCatg);
1450:
1451: return oACLGroups;
1452: } // getACLGroups
1453:
1454: // ----------------------------------------------------------
1455:
1456: /**
1457: * Get Users with direct permissions over this Category.
1458: * @param oConn Database Connection
1459: * @return A DBSubset with 2 columns: gu_user, acl_mask
1460: * @throws SQLException
1461: */
1462: public DBSubset getACLUsers(JDCConnection oConn)
1463: throws SQLException {
1464: Object aCatg[] = { get("id_category") };
1465:
1466: oACLUsers = new DBSubset(DB.k_x_cat_user_acl, DB.gu_user + ","
1467: + DB.acl_mask, DB.gu_category + "=?", 100);
1468: oACLUsers.load(oConn, aCatg);
1469:
1470: return oACLUsers;
1471: } // getACLUsers
1472:
1473: // ----------------------------------------------------------
1474:
1475: /**
1476: * <p>Set New Parent for this category.</p>
1477: * The old parent (if any) is not changed nor removed.<br>
1478: * If Category is already a child of selected parent method proceeds silently
1479: * and no error is raised.
1480: * @param oConn Database Connection
1481: * @param sIdParent GUID of parent Category
1482: * @throws SQLException
1483: */
1484: public void setParent(Connection oConn, String sIdParent)
1485: throws SQLException {
1486: Statement oStmt = oConn.createStatement();
1487: ResultSet oRSet;
1488: String sSQL;
1489: boolean bAlreadyExists;
1490:
1491: if (DebugFile.trace) {
1492: DebugFile.writeln("Begin Category.setParent([Connection], "
1493: + sIdParent + ")");
1494: DebugFile.incIdent();
1495: }
1496:
1497: oStmt = oConn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
1498: ResultSet.CONCUR_READ_ONLY);
1499: sSQL = "SELECT NULL FROM " + DB.k_cat_tree + " WHERE "
1500: + DB.gu_parent_cat + "='" + sIdParent + "' AND "
1501: + DB.gu_child_cat + "='" + getString(DB.gu_category)
1502: + "'";
1503: if (DebugFile.trace)
1504: DebugFile.writeln("Statement.executeQuery(" + sSQL + ")");
1505: oRSet = oStmt.executeQuery(sSQL);
1506: bAlreadyExists = oRSet.next();
1507: oRSet.close();
1508: oStmt.close();
1509:
1510: if (!bAlreadyExists) {
1511: oStmt = oConn.createStatement();
1512: sSQL = "INSERT INTO " + DB.k_cat_tree + " ("
1513: + DB.gu_parent_cat + "," + DB.gu_child_cat
1514: + ") VALUES ('" + sIdParent + "','"
1515: + getString(DB.gu_category) + "')";
1516: if (DebugFile.trace)
1517: DebugFile.writeln("Statement.execute(" + sSQL + ")");
1518: oStmt.execute(sSQL);
1519: oStmt.close();
1520: }
1521: if (DebugFile.trace) {
1522: DebugFile.decIdent();
1523: DebugFile.writeln("End Category.setParent()");
1524: }
1525: } // setParent
1526:
1527: // ----------------------------------------------------------
1528:
1529: /**
1530: * <p>Remove Category from parent.</p>
1531: * Removing a Category from a parent does not delete it.
1532: * @param oConn Database Connection
1533: * @param sIdParent Parent Category GUID
1534: * @throws SQLException
1535: */
1536: public void resetParent(Connection oConn, String sIdParent)
1537: throws SQLException {
1538: Statement oStmt = oConn.createStatement();
1539: String sSQL;
1540:
1541: if (DebugFile.trace) {
1542: DebugFile
1543: .writeln("Begin Category.resetParent([Connection], "
1544: + sIdParent + ")");
1545: DebugFile.incIdent();
1546: }
1547:
1548: sSQL = "DELETE FROM " + DB.k_cat_tree + " WHERE "
1549: + DB.gu_parent_cat + "='" + sIdParent + "' AND "
1550: + DB.gu_child_cat + "='" + getString(DB.gu_category)
1551: + "'";
1552:
1553: if (DebugFile.trace)
1554: DebugFile.writeln("oStmt.executeUpdate(" + sSQL + ")");
1555:
1556: oStmt.executeUpdate(sSQL);
1557:
1558: if (DebugFile.trace) {
1559: DebugFile.decIdent();
1560: DebugFile.writeln("End Category.resetParent()");
1561: }
1562: } // resetParent
1563:
1564: // ----------------------------------------------------------
1565:
1566: /**
1567: * <p>Store Category.</p>
1568: * If gu_category is null a new GUID is automatically assigned.<br>
1569: * dt_modified field is set to current date.
1570: * @param oConn Database Connection
1571: * @throws SQLException
1572: */
1573: public boolean store(JDCConnection oConn) throws SQLException {
1574: java.sql.Timestamp dtNow = new java.sql.Timestamp(DBBind
1575: .getTime());
1576:
1577: // Si no se especificó un identificador para la categoria
1578: // entonces añadirlo autimaticamente
1579: if (!AllVals.containsKey(DB.gu_category))
1580: put(DB.gu_category, Gadgets.generateUUID());
1581:
1582: // Forzar la fecha de modificación del registro
1583: replace(DB.dt_modified, dtNow);
1584:
1585: return super .store(oConn);
1586: } // store
1587:
1588: // ----------------------------------------------------------
1589:
1590: /**
1591: * <p>Expand all Category childs.</p>
1592: * Calls k_sp_cat_expand stored procedure.<br>
1593: * Expansion tree is stored at k_cat_expand table.
1594: * @param oConn Database Connection
1595: * @throws SQLException
1596: */
1597: public void expand(Connection oConn) throws SQLException {
1598: CallableStatement oStmt;
1599:
1600: if (DebugFile.trace) {
1601: DebugFile.writeln("Begin Category.expand([Connection])");
1602: DebugFile.incIdent();
1603: DebugFile
1604: .writeln("Connection.prepareCall({ call k_sp_cat_expand ('"
1605: + getStringNull(DB.gu_category, "null")
1606: + "')}");
1607: }
1608:
1609: oStmt = oConn.prepareCall("{ call k_sp_cat_expand ('"
1610: + getString(DB.gu_category) + "') }");
1611: oStmt.execute();
1612: oStmt.close();
1613:
1614: if (DebugFile.trace) {
1615: DebugFile.decIdent();
1616: DebugFile.writeln("End Category.expand()");
1617: }
1618: } // expand()
1619:
1620: // ----------------------------------------------------------
1621:
1622: /**
1623: * <p>Store a set of labels for this category</p>
1624: * This method takes a string of the form "en;Root|es;Raíz|fr;Racine|it;Radice|ru;\u041A\u043E\u0440\u0435\u043D\u044C"
1625: * and store one label for each {language,literal} pair
1626: * @param oConn JDCConnection
1627: * @param sNamesTable String Language names and translated names
1628: * @param sRowDelim String Delimiter for {language,literal} pairs,
1629: * in the example above it would be "|"
1630: * @param sColDelim String Delimiter between language and literal,
1631: * in the example above it would be ";"
1632: * @throws SQLException
1633: * @throws NoSuchElementException
1634: */
1635: public void storeLabels(JDCConnection oConn, String sNamesTable,
1636: String sRowDelim, String sColDelim) throws SQLException,
1637: NoSuchElementException {
1638: String sName;
1639: String sLanguageId;
1640: String sTrCategory;
1641: int iTokCount;
1642: StringTokenizer oRowTok;
1643: StringTokenizer oColTok;
1644: CategoryLabel oName = new CategoryLabel();
1645:
1646: if (DebugFile.trace) {
1647: DebugFile
1648: .writeln("Begin Category.storeLabels([Connection], \""
1649: + sNamesTable
1650: + "\",\""
1651: + sRowDelim
1652: + "\",\"" + sColDelim + "\")");
1653: DebugFile.incIdent();
1654: }
1655:
1656: if (sNamesTable.length() > 0) {
1657: oName.put(DB.gu_category, getString(DB.gu_category));
1658:
1659: // Sacar el idioma y la lista de etiquetas del String recibido como parametro.
1660:
1661: if (DebugFile.trace)
1662: DebugFile.writeln("new StringTokenizer(" + sNamesTable
1663: + "\"" + sRowDelim + "\"");
1664:
1665: oRowTok = new StringTokenizer(sNamesTable, sRowDelim);
1666:
1667: iTokCount = oRowTok.countTokens();
1668:
1669: if (DebugFile.trace)
1670: DebugFile.writeln(String.valueOf(iTokCount)
1671: + " tokens found");
1672:
1673: for (int r = 0; r < iTokCount; r++) {
1674: // Separar los registros
1675: sName = oRowTok.nextToken();
1676:
1677: if (DebugFile.trace)
1678: DebugFile.writeln("new StringTokenizer(" + sName
1679: + ", \"" + sColDelim + "\"");
1680:
1681: // Para cada registro separar los campos
1682: oColTok = new StringTokenizer(sName, sColDelim);
1683:
1684: if (DebugFile.trace)
1685: DebugFile.writeln("StringTokenizer.nextToken("
1686: + String.valueOf(r) + ") for id_language");
1687:
1688: sLanguageId = oColTok.nextToken();
1689:
1690: if (DebugFile.trace)
1691: DebugFile.writeln("StringTokenizer.nextToken("
1692: + String.valueOf(r) + ") for tr_category");
1693:
1694: sTrCategory = oColTok.nextToken();
1695:
1696: if (sTrCategory != null) {
1697: sTrCategory = sTrCategory.trim();
1698:
1699: if (sTrCategory.length() > 0) {
1700: oName.replace(DB.id_language, sLanguageId);
1701: oName.replace(DB.tr_category, sTrCategory);
1702:
1703: if (DebugFile.trace)
1704: DebugFile.writeln("CategoryLabel.store("
1705: + sLanguageId + "," + sTrCategory
1706: + ")");
1707:
1708: oName.store(oConn);
1709: }
1710: } // fi (tr_category!=null)
1711: } // endfor (r)
1712: } // fi (sNamesTable!="")
1713:
1714: if (DebugFile.trace) {
1715: DebugFile.decIdent();
1716: DebugFile.writeln("End Category.storeLabels()");
1717: }
1718: } // storeLabels()
1719:
1720: // ----------------------------------------------------------
1721:
1722: /**
1723: * <p>Copy a directory and index all its files as products inside this Category</p>
1724: * @param oConn JDCConnection Any pending transaction on given connection will be commited.
1725: * This methods calls Connection.commit() on oConn object,
1726: * so AutoCommit status for connection must be set to true before calling uploadDirectory()
1727: * @param sSourcePath String "file:///tmp/upload/myfiles"
1728: * @param sProtocol String "file://"
1729: * @param sServer String Server name (for FTP transfers)
1730: * @param sTargetPath String "file:///opt/hipergate/storege/domians/2050/..."
1731: * @param sLanguage String
1732: * @throws Exception
1733: * @throws IOException
1734: * @throws SQLException
1735: */
1736: public void uploadDirectory(JDCConnection oConn,
1737: String sSourcePath, String sProtocol, String sServer,
1738: String sTargetPath, String sLanguage) throws Exception,
1739: IOException, SQLException {
1740: File oDir, oFile;
1741: File aFiles[];
1742: int iFiles;
1743: String sFileName, sBasePath, sTargetChomp, sNewCategoryId, sNewCategoryNm;
1744: Properties oURLProps;
1745: FileInputStream oIOStrm;
1746: PreparedStatement oStmt, oCatg;
1747: ResultSet oRSet;
1748:
1749: Category oNewCategory;
1750: Product oProd;
1751: ProductLocation oLoca;
1752:
1753: Object aCatValues[];
1754: Object aLblValues[];
1755: Short iTrue = new Short((short) 1);
1756: Integer iActive = new Integer(1);
1757:
1758: if (DebugFile.trace) {
1759: DebugFile
1760: .writeln("Begin Category.uploadDirectory([Connection], "
1761: + sSourcePath
1762: + ", ..., "
1763: + sTargetPath
1764: + "," + sLanguage + ")");
1765: DebugFile.incIdent();
1766: }
1767:
1768: if (null == oFS) {
1769: if (DebugFile.trace)
1770: DebugFile.writeln("new com.knowgate.dfs.FileSystem()");
1771: oFS = new FileSystem();
1772: }
1773:
1774: // Crea la ruta base quitando el file:// de por delante
1775: sBasePath = sSourcePath
1776: .substring(sSourcePath.indexOf("://") + 3);
1777:
1778: if (DebugFile.trace)
1779: DebugFile.writeln("sBasePath=" + sBasePath);
1780:
1781: oProd = new Product();
1782: oProd.put(DB.gu_owner, getString(DB.gu_owner));
1783:
1784: // Obtiene un array con los archivos del directorio base
1785: oDir = new File(sBasePath);
1786: aFiles = oDir.listFiles();
1787: iFiles = aFiles.length;
1788:
1789: if (DebugFile.trace)
1790: DebugFile.writeln(String.valueOf(iFiles) + " files found");
1791:
1792: // Cursor preparado para leer los archivos de una categoría
1793: if (DebugFile.trace)
1794: DebugFile
1795: .writeln("Connection.prepareStatement(SELECT NULL FROM "
1796: + DB.k_products
1797: + " p, "
1798: + DB.k_x_cat_objs
1799: + " o WHERE p."
1800: + DB.nm_product
1801: + "=? AND o."
1802: + DB.gu_category
1803: + "=? AND o."
1804: + DB.id_class
1805: + "="
1806: + String.valueOf(Product.ClassId)
1807: + " AND p."
1808: + DB.gu_product
1809: + "=o."
1810: + DB.gu_object);
1811: oStmt = oConn
1812: .prepareStatement("SELECT " + DB.gu_product + " FROM "
1813: + DB.k_products + " p, " + DB.k_x_cat_objs
1814: + " o WHERE p." + DB.nm_product + "=? AND o."
1815: + DB.gu_category + "=? AND o." + DB.id_class
1816: + "=" + String.valueOf(Product.ClassId)
1817: + " AND p." + DB.gu_product + "=o."
1818: + DB.gu_object, ResultSet.TYPE_FORWARD_ONLY,
1819: ResultSet.CONCUR_READ_ONLY);
1820:
1821: // Cursor preparado para buscar una categoría hija por nombre traducido
1822: if (DebugFile.trace)
1823: DebugFile
1824: .writeln("Connection.prepareStatement(SELECT "
1825: + DB.gu_category
1826: + " FROM "
1827: + DB.k_cat_tree
1828: + " t,"
1829: + DB.k_cat_labels
1830: + " l WHERE t.gu_parent_cat=? AND l.tr_category=? AND t.gu_child_cat=l.gu_category");
1831: oCatg = oConn.prepareStatement("SELECT " + DB.gu_category
1832: + " FROM " + DB.k_cat_tree + " t," + DB.k_cat_labels
1833: + " l WHERE t." + DB.gu_parent_cat + "=? AND l."
1834: + DB.tr_category + "=? AND t." + DB.gu_child_cat
1835: + "=l." + DB.gu_category, ResultSet.TYPE_FORWARD_ONLY,
1836: ResultSet.CONCUR_READ_ONLY);
1837:
1838: for (int f = 0; f < iFiles; f++) {
1839: oFile = aFiles[f];
1840: if (oFile.isFile()) {
1841: sFileName = oFile.getName();
1842:
1843: if (DebugFile.trace)
1844: DebugFile.writeln("nm_product="
1845: + (sFileName.length() <= 128 ? sFileName
1846: : sFileName.substring(0, 128)));
1847:
1848: if (sFileName.toLowerCase().endsWith(".url"))
1849: // Si el archivo tiene extensión .url interpretarlo como un enlace
1850: oProd.put(DB.nm_product,
1851: (sFileName.length() <= 128 ? sFileName
1852: .substring(0,
1853: sFileName.length() - 4)
1854: : sFileName.substring(0, 128)));
1855: else
1856: // Es un archivo físico
1857: oProd.put(DB.nm_product,
1858: (sFileName.length() <= 128 ? sFileName
1859: : sFileName.substring(0, 128)));
1860:
1861: // Guardar el archivo como un producto en la base de datos
1862: oStmt.setString(1, oProd.getString(DB.nm_product));
1863: oStmt.setString(2, getString(DB.gu_category));
1864: oRSet = oStmt.executeQuery();
1865: if (oRSet.next())
1866: oProd.put(DB.gu_product, oRSet.getString(1));
1867: oRSet.close();
1868:
1869: if (DebugFile.trace)
1870: DebugFile.writeln("oProd.store([Connection]);");
1871: oProd.store(oConn);
1872:
1873: // Añadir el producto a la categoría actual
1874: if (DebugFile.trace)
1875: DebugFile
1876: .writeln("oProd.addToCategory([Connection], "
1877: + getStringNull(DB.gu_category,
1878: "null") + ");");
1879: oProd.addToCategory(oConn, this
1880: .getString(DB.gu_category), 0);
1881:
1882: // Crear una nueva ubicación de producto para apuntar al archivo físico o al enlace
1883: oLoca = new ProductLocation();
1884:
1885: oLoca.put(DB.gu_owner, getString(DB.gu_owner));
1886: oLoca.put(DB.gu_product, oProd.get(DB.gu_product));
1887:
1888: if (sFileName.toLowerCase().endsWith(".url")) {
1889: // Si se trata de un archivo .url
1890: // abrirlo como un fichero de propiedades
1891: // para sacar los parámetros del enlace.
1892: oURLProps = new Properties();
1893: oIOStrm = new FileInputStream(oFile);
1894: oURLProps.load(oIOStrm);
1895: oIOStrm.close();
1896:
1897: if (DebugFile.trace)
1898: DebugFile.writeln("URL="
1899: + oURLProps.getProperty("URL", "null"));
1900:
1901: oLoca.setURL(oURLProps.getProperty("URL"));
1902: oLoca.remove(DB.xfile);
1903: oLoca.remove(DB.xoriginalfile);
1904:
1905: oLoca
1906: .put(DB.id_cont_type, oLoca
1907: .getContainerType());
1908: oLoca.setLength(0);
1909: if (DebugFile.trace)
1910: DebugFile.writeln("oLoca.store([Connection])");
1911: oLoca.store(oConn);
1912:
1913: try {
1914: oConn.commit();
1915: } catch (SQLException ignore) { /* Ignore exception if AutoCOmmit was already set to true*/
1916: }
1917: } else {
1918: // Si es una archivo físico moverlo de ubicación y apuntar su ruta en la base de datos
1919: oLoca.setPath(sProtocol, sServer, sTargetPath,
1920: sFileName, sFileName);
1921: oLoca
1922: .put(DB.id_cont_type, oLoca
1923: .getContainerType());
1924: oLoca
1925: .setLength(new Long(oFile.length())
1926: .intValue());
1927:
1928: if (DebugFile.trace)
1929: DebugFile.writeln("oLoca.store([Connection])");
1930: oLoca.store(oConn);
1931:
1932: try {
1933: oConn.commit();
1934: } catch (SQLException ignore) { /* Ignore exception if AutoCOmmit was already set to true*/
1935: }
1936:
1937: // Coger el fichero "sSourcePath/sFileName" y moverlo a "sProtocol://sServer/sTargetPath/sFileName"
1938: // luego grabar en la base de datos su nueva ubicación física
1939: oLoca.upload(oConn, oFS, sSourcePath, sFileName,
1940: sProtocol + sServer + sTargetPath,
1941: sFileName);
1942: }
1943: oLoca = null;
1944:
1945: oProd.remove(DB.gu_product);
1946: oProd.remove(DB.nm_product);
1947: } else if (oFile.isDirectory()) {
1948: sFileName = oFile.getName();
1949:
1950: if (sProtocol.startsWith("file://"))
1951: sTargetChomp = (sTargetPath.endsWith(System
1952: .getProperty("file.separator")) ? sTargetPath
1953: : sTargetPath
1954: + System
1955: .getProperty("file.separator"));
1956: else
1957: sTargetChomp = (sTargetPath.endsWith("/") ? sTargetPath
1958: : sTargetPath + "/");
1959:
1960: oCatg.setString(1, getString(DB.gu_category));
1961: oCatg.setString(2, sFileName);
1962: oRSet = oCatg.executeQuery();
1963: if (oRSet.next()) {
1964: sNewCategoryId = oRSet.getString(1);
1965: oNewCategory = new Category(oConn, sNewCategoryId);
1966: sNewCategoryNm = oNewCategory
1967: .getString(DB.nm_category);
1968:
1969: // Crear el directorio espejo donde se almacenan los archivos (Productos) de la categoría
1970: oFS.mkdirs(sProtocol + sServer + sTargetChomp
1971: + sNewCategoryNm);
1972: } else {
1973:
1974: // Componer un nuevo alias (nombre corto único) para la categoria que representa el directorio
1975: sNewCategoryNm = Category
1976: .makeName(oConn, sFileName);
1977:
1978: if (DebugFile.trace)
1979: DebugFile.writeln("sNewCategoryNm="
1980: + sNewCategoryNm);
1981:
1982: // Crear la categoría
1983: aCatValues = new Object[] {
1984: getString(DB.gu_category),
1985: getString(DB.gu_owner), sNewCategoryNm,
1986: iTrue, iActive, "folderclosed_16x16.gif",
1987: "folderopen_16x16.gif" };
1988: sNewCategoryId = Category.create(oConn, aCatValues);
1989:
1990: // Crear la etiqueta de nombre traducido para la categoría
1991: aLblValues = new Object[] { sNewCategoryId,
1992: sLanguage, sFileName, null };
1993: CategoryLabel.create(oConn, aLblValues);
1994:
1995: try {
1996: oConn.commit();
1997: } catch (SQLException ignore) { /* Ignore exception if AutoCOmmit was already set to true*/
1998: }
1999:
2000: // Crear el directorio espejo donde se almacenan los archivos (Productos) de la categoría
2001: oFS.mkdirs(sProtocol + sServer + sTargetChomp
2002: + sNewCategoryNm);
2003:
2004: // Añadir archivos recursivamente
2005: oNewCategory = new Category(oConn, sNewCategoryId);
2006: oNewCategory.inheritPermissions(oConn,
2007: getString(DB.gu_category), (short) 1,
2008: (short) 1);
2009: }
2010: oRSet.close();
2011:
2012: oNewCategory.uploadDirectory(oConn, "file://"
2013: + oFile.getAbsolutePath(), sProtocol, sServer,
2014: sTargetChomp + sNewCategoryNm, sLanguage);
2015: }
2016: }
2017:
2018: if (DebugFile.trace) {
2019: DebugFile.decIdent();
2020: DebugFile.writeln("End Category.uploadDirectory()");
2021: }
2022:
2023: } // uploadDirectory()
2024:
2025: // **********************************************************
2026: // Static Methods
2027:
2028: /**
2029: * <p>Create or Store Category.</p>
2030: * @param oConn Database Connection
2031: * @param sCategoryId GUID of Category to store or <b>null</b> if it is a new Category.
2032: * @param sParentId GUID of Parent Category or <b>null</b> if it is a root category.
2033: * @param sCategoryName Internal Category Name. It is recommended that makeName()
2034: * method is applied always on sCategoryName. Because category names are often used for composing
2035: * physical disk paths, assigning characters such as '*', '/', '?' etc. to category names may
2036: * lead to errors when creating directories for contained Products. As a general rule use ONLY
2037: * upper case letters and numbers for category names.
2038: * @param iIsActive 1 if category is to be marked active, 0 if it is to be marked as unactive.
2039: * @param iDocStatus Initial Document Status, { 0=Pending, 1=Active, 2=Locked } See k_lu_status table.
2040: * @param sOwner GUID of User owner of this Category.
2041: * @param sIcon1 Icon for closed folder.
2042: * @param sIcon2 Icon for opened folder.
2043: * @return GUID of new Category or sCategoryId if Category already existed.
2044: * @throws SQLException
2045: */
2046: public static String store(JDCConnection oConn, String sCategoryId,
2047: String sParentId, String sCategoryName, short iIsActive,
2048: int iDocStatus, String sOwner, String sIcon1, String sIcon2)
2049: throws SQLException {
2050: Category oCatg = new Category();
2051: boolean isParentOfParent = false;
2052: Object aCatg[] = { null };
2053: DBSubset oNames;
2054:
2055: if (DebugFile.trace) {
2056: DebugFile.writeln("Begin Category.store([Connection], "
2057: + sCategoryId + ", " + sParentId
2058: + ", sCategoryName" + ", ...)");
2059: DebugFile.incIdent();
2060: }
2061:
2062: oCatg.put(DB.gu_owner, sOwner);
2063: oCatg.put(DB.nm_category, sCategoryName);
2064: oCatg.put(DB.bo_active, iIsActive);
2065: oCatg.put(DB.id_doc_status, iDocStatus);
2066:
2067: if (null != sIcon1)
2068: oCatg.put(DB.nm_icon, sIcon1);
2069: if (null != sIcon2)
2070: oCatg.put(DB.nm_icon2, sIcon2);
2071:
2072: if (null != sCategoryId) {
2073: oCatg.put(DB.gu_category, sCategoryId);
2074:
2075: // Verificar que la categoria no es padre de si misma
2076: if (null != sParentId) {
2077: if (sCategoryId.equalsIgnoreCase(sParentId)) {
2078: if (DebugFile.trace)
2079: DebugFile.writeln("ERROR: Category "
2080: + sCategoryName + " is its own parent");
2081: throw new SQLException(
2082: "Category tree circular reference");
2083: } // endif (sCategoryId==sParentId)
2084:
2085: // Si la categoria tiene padre (no es raiz) entonces
2086: // verificar que el padre no es a su vez un hijo de
2087: // la categoria para evitar la creacion de bucles.
2088: isParentOfParent = oCatg.isParentOf(oConn, sParentId);
2089: } // endif (sParentId)
2090:
2091: } // endif (null!=sCategoryId)
2092:
2093: if (isParentOfParent) {
2094: if (DebugFile.trace)
2095: DebugFile.writeln("ERROR: Category " + sCategoryName
2096: + " has a circular parentship relationship");
2097: throw new SQLException("Category tree circular reference");
2098: }
2099:
2100: // Si la categoria ya existia, entonces
2101: // borrar todos los nombres traducidos (etiquetas)
2102: if (null != sCategoryId) {
2103: if (DebugFile.trace)
2104: DebugFile.writeln("Clearing labels...");
2105: aCatg[0] = oCatg.getString(DB.gu_category);
2106: oNames = new DBSubset(DB.k_cat_labels, DB.id_language + ","
2107: + DB.tr_category + "," + DB.url_category,
2108: DB.gu_category + "=?", 1);
2109: oNames.clear(oConn, aCatg);
2110: if (DebugFile.trace)
2111: DebugFile.writeln("Labels cleared.");
2112: } else
2113: oCatg.remove(DB.gu_category);
2114:
2115: // Grabar la categoria,
2116: // si el campo id_category no existe (nueva categoria)
2117: // el metodo store lo rellenara automaticamente al grabar
2118: oCatg.store(oConn);
2119:
2120: // Establecer si la categoria es raiz
2121: if (null == sParentId)
2122: oCatg.setIsRoot(oConn, true);
2123: else
2124: oCatg.setParent(oConn, sParentId);
2125:
2126: if (DebugFile.trace) {
2127: DebugFile.decIdent();
2128: DebugFile.writeln("End Category.store() : "
2129: + oCatg.getString(DB.gu_category));
2130: }
2131:
2132: // Recuperar el identificador unico de la categoria recien escrita
2133: return oCatg.getString(DB.gu_category);
2134: } // storeCategory()
2135:
2136: // ----------------------------------------------------------
2137:
2138: /**
2139: * <p>Delete Category and all its childs.</p>
2140: * First delete all Products and Companies contained in Category, including
2141: * physical disk files associted with Products and Company attachments.<br>
2142: * Then call k_sp_del_category_r stored procedure and perform recursive
2143: * deletion of all childs.
2144: * @param oConn Database Connection
2145: * @param sCategoryGUID GUID of Category to delete.
2146: * @throws SQLException
2147: */
2148: public static boolean delete(JDCConnection oConn,
2149: String sCategoryGUID) throws SQLException, IOException {
2150:
2151: if (DebugFile.trace) {
2152: DebugFile.writeln("Begin Category.delete([Connection], "
2153: + sCategoryGUID + ")");
2154: DebugFile.incIdent();
2155: }
2156:
2157: Category oCat = new Category(sCategoryGUID);
2158:
2159: // Delete any child category first
2160: // New for v2.1
2161: DBSubset oChlds = oCat.getChilds(oConn);
2162: int iChilds = oChlds.getRowCount();
2163: for (int c = 0; c < iChilds; c++)
2164: Category.delete(oConn, oChlds.getString(0, c));
2165:
2166: Statement oStmt;
2167: Product oProd;
2168: DBSubset oObjs = oCat.getObjects(oConn);
2169: int iObjs = oObjs.getRowCount();
2170: boolean bRetVal;
2171:
2172: // recorre los objetos de esta categoría y los borra
2173: for (int o = 0; o < iObjs; o++) {
2174: switch (oObjs.getInt(1, o)) {
2175: case com.knowgate.hipergate.Product.ClassId:
2176: oProd = new Product(oConn, oObjs.getString(0, o));
2177: oProd.delete(oConn);
2178: break;
2179: case com.knowgate.crm.Company.ClassId:
2180: com.knowgate.crm.Company.delete(oConn, oObjs.getString(
2181: 0, o));
2182: break;
2183: case com.knowgate.forums.NewsGroup.ClassId:
2184: com.knowgate.forums.NewsGroup.delete(oConn, oObjs
2185: .getString(0, o));
2186: break;
2187: case com.knowgate.hipergate.Image.ClassId:
2188: Image oImg = new com.knowgate.hipergate.Image(oConn,
2189: oObjs.getString(0, o));
2190: oImg.delete(oConn);
2191: break;
2192: case com.knowgate.hipermail.DBMimeMessage.ClassId:
2193: com.knowgate.hipermail.DBMimeMessage.delete(oConn,
2194: sCategoryGUID, oObjs.getString(0, o));
2195: break;
2196: }
2197: } // next (o)
2198:
2199: oObjs = null;
2200:
2201: if (DBBind.exists(oConn, DB.k_mime_msgs, "U")) {
2202: oObjs = new DBSubset(DB.k_mime_msgs, DB.gu_mimemsg,
2203: DB.gu_category + "='" + sCategoryGUID + "'", 1000);
2204: iObjs = oObjs.load(oConn);
2205:
2206: if (oConn.getDataBaseProduct() == JDCConnection.DBMS_POSTGRESQL) {
2207: PreparedStatement oDlte = oConn
2208: .prepareStatement("SELECT k_sp_del_mime_msg(?)");
2209: ResultSet oRSet;
2210: for (int m = 0; m < iObjs; m++) {
2211: oDlte.setString(1, oObjs.getString(0, m));
2212: oRSet = oDlte.executeQuery();
2213: oRSet.close();
2214: }
2215: oDlte.close();
2216: } else {
2217: CallableStatement oCall = oConn
2218: .prepareCall("{ call k_sp_del_mime_msg(?) }");
2219: for (int m = 0; m < iObjs; m++) {
2220: oCall.setString(1, oObjs.getString(0, m));
2221: oCall.execute();
2222: }
2223: oCall.close();
2224: }
2225: } // fi (exists(k_mime_msgs))
2226:
2227: oObjs = null;
2228:
2229: if (DBBind.exists(oConn, DB.k_x_company_prods, "U")) {
2230:
2231: oStmt = oConn.createStatement();
2232:
2233: if (DebugFile.trace)
2234: DebugFile
2235: .writeln("Statement.executeUpdate(DELETE FROM "
2236: + DB.k_x_company_prods + " WHERE "
2237: + DB.gu_category + "='" + sCategoryGUID
2238: + "')");
2239:
2240: oStmt.executeUpdate("DELETE FROM " + DB.k_x_company_prods
2241: + " WHERE " + DB.gu_category + "='" + sCategoryGUID
2242: + "'");
2243:
2244: oStmt.close();
2245: } // fi (k_x_company_prods)
2246:
2247: if (DBBind.exists(oConn, DB.k_x_contact_prods, "U")) {
2248:
2249: oStmt = oConn.createStatement();
2250:
2251: if (DebugFile.trace)
2252: DebugFile
2253: .writeln("Statement.executeUpdate(DELETE FROM "
2254: + DB.k_x_contact_prods + " WHERE "
2255: + DB.gu_category + "='" + sCategoryGUID
2256: + "')");
2257:
2258: oStmt.executeUpdate("DELETE FROM " + DB.k_x_contact_prods
2259: + " WHERE " + DB.gu_category + "='" + sCategoryGUID
2260: + "'");
2261:
2262: oStmt.close();
2263: } // fi (k_x_contact_prods)
2264:
2265: // Saca la lista de categorías hijas de primer nivel y repite el proceso de borrado
2266: LinkedList oChilds = oCat.browse(oConn, BROWSE_DOWN,
2267: BROWSE_BOTTOMUP);
2268: ListIterator oIter = oChilds.listIterator();
2269:
2270: while (oIter.hasNext()) {
2271: oCat = (Category) oIter.next();
2272: oCat.delete(oConn);
2273: } // wend
2274:
2275: if (oConn.getDataBaseProduct() == JDCConnection.DBMS_POSTGRESQL) {
2276: if (DebugFile.trace)
2277: DebugFile
2278: .writeln("Connection.executeQuery(SELECT k_sp_del_category_r ('"
2279: + sCategoryGUID + "'))");
2280: oStmt = oConn.createStatement();
2281: oStmt.executeQuery("SELECT k_sp_del_category_r ('"
2282: + sCategoryGUID + "')");
2283: oStmt.close();
2284: bRetVal = true;
2285: } else {
2286: if (DebugFile.trace)
2287: DebugFile
2288: .writeln("Connection.prepareCall({call k_sp_del_category_r('"
2289: + sCategoryGUID + "')})");
2290: CallableStatement oCall = oConn
2291: .prepareCall("{call k_sp_del_category_r ('"
2292: + sCategoryGUID + "')}");
2293: bRetVal = oCall.execute();
2294: oCall.close();
2295: }
2296:
2297: if (DebugFile.trace) {
2298: DebugFile.decIdent();
2299: DebugFile.writeln("End Category.delete() : "
2300: + String.valueOf(bRetVal));
2301: }
2302: return bRetVal;
2303: } // delete
2304:
2305: // ----------------------------------------------------------
2306:
2307: /**
2308: * Create new Category with translated labels.
2309: * @param oConn Database Connection
2310: * @param Values An array with the following elements:<br>
2311: * { (String) gu_parent, (String) gu_owner, (String) nm_category,
2312: * (Short) bo_active, (Short) id_doc_status, (String) nm_icon, (String) nm_icon2 }
2313: * @return GUID of new Category
2314: * @throws SQLException
2315: */
2316: public static String create(JDCConnection oConn, Object[] Values)
2317: throws SQLException {
2318: Category oCatg = new Category();
2319:
2320: oCatg.put(DB.gu_owner, Values[1]);
2321: oCatg.put(DB.nm_category, Values[2]);
2322: oCatg.put(DB.bo_active, Values[3]);
2323: oCatg.put(DB.id_doc_status, Values[4]);
2324: oCatg.put(DB.nm_icon, Values[5]);
2325: if (Values[6] != null)
2326: oCatg.put(DB.nm_icon2, Values[6]);
2327: else
2328: oCatg.put(DB.nm_icon2, Values[5]);
2329:
2330: // Grabar la categoria, el metodo store
2331: // rellenara automaticamente el campo gu_category
2332: oCatg.store(oConn);
2333:
2334: // Establecer el padre
2335: if (null != Values[0])
2336: oCatg.setParent(oConn, Values[0].toString());
2337: else
2338: oCatg.setIsRoot(oConn, true);
2339:
2340: // Recuperar el identificador unico de la categoria recien escrita
2341: return oCatg.getString(DB.gu_category);
2342: } // create()
2343:
2344: // ----------------------------------------------------------
2345:
2346: /**
2347: * <p>Get Category GUID given its internal name.</p>
2348: * Category name is column nm_category at table k_categories.<br>
2349: * This Java method calls k_sp_get_cat_id database stored procedure.
2350: * @param oConn Database Connection
2351: * @param sCategoryNm Category Internal Name
2352: * @return Category GUID or <b>null</b> if no Category with such name is found.
2353: * @throws SQLException
2354: */
2355: public static String getIdFromName(JDCConnection oConn,
2356: String sCategoryNm) throws SQLException {
2357: if (DebugFile.trace) {
2358: DebugFile
2359: .writeln("Begin Category.getIdFromName([Connection], "
2360: + sCategoryNm + ")");
2361: DebugFile.incIdent();
2362: }
2363:
2364: String sCatId = DBPersist.getUIdFromName(oConn, null,
2365: sCategoryNm, "k_sp_get_cat_id");
2366:
2367: if (DebugFile.trace) {
2368: DebugFile.decIdent();
2369: DebugFile.writeln("End Category.getIdFromName() : "
2370: + sCatId);
2371: }
2372:
2373: return sCatId;
2374: } // getIdFromName()
2375:
2376: // ----------------------------------------------------------
2377:
2378: /**
2379: * <p>Make an internal category name from an arbitrary string.</p>
2380: * Because nm_category is a primary key for table k_categories and because
2381: * category names are used for composing physical disk paths, some special
2382: * rules must be followed when assigning category names.<br>
2383: * <ul>
2384: * <li><b>First</b> a Category Name MUST NOT containing any character not allowed in a directory name.
2385: * <li><b>Second</b> a Category Name MUST be unique for all categories at all WorkAreas.
2386: * </ul>
2387: * @param oConn Database Connection
2388: * @param sCategoryNm String to be used as a guide for making category name.
2389: * @return The input string truncated to 18 characters and transformed to upper case.
2390: * Method Gadgets.ASCIIEncode() is applies and spaces, commas, asterisks, slashes,
2391: * backslashes and other characters are removed or substituted.
2392: * Finally an 8 decimals integer tag is appended to name for making it unique.
2393: * For example "Barnes & Noble" is transormed to "BARNES_A_NOBLE~00000001"
2394: * @throws SQLException
2395: */
2396: public static synchronized String makeName(JDCConnection oConn,
2397: String sCategoryNm) throws SQLException {
2398: String sCatNm;
2399: int iChurriguito;
2400: String sCatIndex;
2401:
2402: if (DebugFile.trace) {
2403: DebugFile.writeln("Begin Category.makeName([Connection], "
2404: + sCategoryNm + ")");
2405: DebugFile.incIdent();
2406: }
2407:
2408: String sShortCategoryNm = (sCategoryNm.length() > 18 ? sCategoryNm
2409: .substring(0, 18)
2410: : sCategoryNm);
2411: sShortCategoryNm = Gadgets.ASCIIEncode(sShortCategoryNm);
2412: sShortCategoryNm.replace(' ', '_');
2413: sShortCategoryNm.replace(',', '_');
2414: sShortCategoryNm.replace(';', '_');
2415: sShortCategoryNm.replace('"', 'q');
2416: sShortCategoryNm.replace('|', '_');
2417:
2418: // Obtener el máximo de las categorías cuyo alias es igual al buscado
2419: if (DebugFile.trace)
2420: DebugFile.writeln("Connection.prepareStatement(SELECT "
2421: + DBBind.Functions.ISNULL + "(MAX("
2422: + DB.nm_category + "),'" + sShortCategoryNm
2423: + "~00000000') FROM " + DB.k_categories + " WHERE "
2424: + DB.nm_category + " LIKE '" + sShortCategoryNm
2425: + "%')");
2426:
2427: PreparedStatement oStmt = oConn.prepareStatement("SELECT "
2428: + DBBind.Functions.ISNULL + "(MAX(" + DB.nm_category
2429: + "),'" + sShortCategoryNm + "~00000000') FROM "
2430: + DB.k_categories + " WHERE " + DB.nm_category
2431: + " LIKE ?", ResultSet.TYPE_FORWARD_ONLY,
2432: ResultSet.CONCUR_READ_ONLY);
2433:
2434: oStmt.setString(1, sShortCategoryNm + "%");
2435: ResultSet oRSet = oStmt.executeQuery();
2436: if (oRSet.next()) {
2437: sCatNm = oRSet.getString(1);
2438: } else
2439: sCatNm = sShortCategoryNm + "~00000000";
2440: oRSet.close();
2441: oStmt.close();
2442:
2443: // Buscar el churriguito y sacar los números que quedan a la derecha
2444: iChurriguito = sCatNm.indexOf("~");
2445: if (iChurriguito > 0)
2446: sCatIndex = String.valueOf(Integer.parseInt(sCatNm
2447: .substring(iChurriguito + 1)) + 1);
2448: else
2449: sCatIndex = "00000001";
2450:
2451: // Añadir zeros de padding por la izquierda
2452: for (int z = 0; z < 8 - sCatIndex.length(); z++)
2453: sCatIndex = "0" + sCatIndex;
2454:
2455: sShortCategoryNm += "~" + sCatIndex;
2456:
2457: if (DebugFile.trace) {
2458: DebugFile.decIdent();
2459: DebugFile.writeln("End Category.makeName() : "
2460: + sShortCategoryNm);
2461: }
2462:
2463: return sShortCategoryNm;
2464: } // makeName
2465:
2466: // **********************************************************
2467: // Constantes Publicas
2468:
2469: public static final int BROWSE_UP = 0;
2470: public static final int BROWSE_DOWN = 1;
2471:
2472: public static final int BROWSE_TOPDOWN = 0;
2473: public static final int BROWSE_BOTTOMUP = 1;
2474:
2475: public static final short ClassId = 10;
2476:
2477: // **********************************************************
2478: // Variables Privadas
2479:
2480: private DBSubset oParents;
2481: private DBSubset oChilds;
2482: private DBSubset oNames;
2483: private DBSubset oACLGroups;
2484: private DBSubset oACLUsers;
2485: private FileSystem oFS;
2486: }
|