/* * Copyright (C) NetStruxr, Inc. All rights reserved. * * This software is published under the terms of the NetStruxr * Public Software License version 0.5, a copy of which has been * included with this distribution in the LICENSE.NPL file. */ package er.corebusinesslogic; import java.util.Enumeration; import org.apache.commons.lang3.ObjectUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.webobjects.eocontrol.EOEditingContext; import com.webobjects.eocontrol.EOEnterpriseObject; import com.webobjects.eocontrol.EOKeyValueArchiver; import com.webobjects.eocontrol.EOKeyValueUnarchiver; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSForwardException; import com.webobjects.foundation.NSKeyValueCoding; import com.webobjects.foundation.NSNotification; import com.webobjects.foundation.NSNotificationCenter; import com.webobjects.foundation.NSPropertyListSerialization; import com.webobjects.foundation.NSSelector; import er.directtoweb.components.repetitions.ERDAttributeRepetition; import er.extensions.batching.ERXBatchNavigationBar; import er.extensions.components.ERXSortOrder; import er.extensions.eof.ERXConstant; import er.extensions.eof.ERXEC; import er.extensions.eof.ERXEOControlUtilities; import er.extensions.foundation.ERXProperties; import er.extensions.foundation.ERXRetainer; import er.extensions.foundation.ERXValueUtilities; /** * * @property er.corebusinesslogic.ERCoreUserPreferences.handlerClassName */ public class ERCoreUserPreferences implements NSKeyValueCoding { // =========================================================================== // Class Constant(s) // --------------------------------------------------------------------------- private static final Logger log = LoggerFactory.getLogger(ERCoreUserPreferences.class); /** EOEncoding key */ private final static String VALUE="_V"; /** Notification that is posted when preferences change */ public final static String PreferenceDidChangeNotification = "PreferenceChangedNotification"; // =========================================================================== // Class Variable(s) // --------------------------------------------------------------------------- /** caches the singleton user preference object */ private static ERCoreUserPreferences _userPreferences; // =========================================================================== // Class Method(s) // --------------------------------------------------------------------------- /** * Gets the singleton instance for interacting with * the user preference system. * @return single instance of the user preferences */ public static ERCoreUserPreferences userPreferences() { if (_userPreferences == null) { _userPreferences = new ERCoreUserPreferences(); } return _userPreferences; } // =========================================================================== // Instance Method(s) // --------------------------------------------------------------------------- /** * Registers notification handlers for user preference notifications. These * are mainly used within the context of D2W pages. */ public void registerHandlers() { log.debug("Registering preference handlers"); Object handler = null; String handlerClassName = ERXProperties.stringForKey("er.corebusinesslogic.ERCoreUserPreferences.handlerClassName"); if (handlerClassName != null) { try { handler = Class.forName(handlerClassName).newInstance(); } catch (Exception e) { throw NSForwardException._runtimeExceptionForThrowable(e); } } if (handler == null) { handler = new _UserPreferenceHandler(); } ERXRetainer.retain(handler); } protected NSArray preferences(EOEditingContext ec) { ERCoreUserInterface user = (ERCoreUserInterface)ERCoreBusinessLogic.actor(ec); return user!=null ? user.preferences() : NSArray.EmptyArray; } protected EOEnterpriseObject preferenceRecordForKey(String key, EOEditingContext ec) { EOEnterpriseObject result=null; if (key != null) { log.debug("Preference value for key = {}", key); for (Enumeration e = preferences(ec).objectEnumerator(); e.hasMoreElements();) { EOEnterpriseObject pref = (EOEnterpriseObject)e.nextElement(); String prefKey = (String)pref.valueForKey("key"); log.debug("prefKey '{}'", prefKey); if (prefKey != null && prefKey.equals(key)) { result = pref; break; } } } return result; } protected String encodedValue(Object value) { EOKeyValueArchiver archiver = new EOKeyValueArchiver(); archiver.encodeObject(value,VALUE); String encodedValue = NSPropertyListSerialization.stringFromPropertyList(archiver.dictionary()); return encodedValue; } protected Object decodedValue(String encodedValue) { NSDictionary d = (NSDictionary )NSPropertyListSerialization.propertyListFromString(encodedValue); EOKeyValueUnarchiver u = new EOKeyValueUnarchiver(d); return u.decodeObjectForKey(VALUE); } // =========================================================================== // Implementation of NSKeyValueCoding // --------------------------------------------------------------------------- // FIXME -- unarchiving - archiving probably could use optimization public Object valueForKey(String key) { Object result=null; EOEditingContext ec = ERXEC.newEditingContext(); ec.lock(); try { EOEnterpriseObject pref = preferenceRecordForKey(key, ec); if (pref != null) { String encodedValue = (String)pref.valueForKey("value"); if(encodedValue !=null) { result = decodedValue(encodedValue); } } } catch(RuntimeException ex) { log.error("Error while getting preference {}", key, ex); } finally { ec.unlock(); } ec.dispose(); log.debug("Prefs vfk {} = {}", key, result); return result; } public void takeValueForKey(Object value, String key) { // we first make sure there is no cruft left // !! locking is turned off on the value attribute of UserPreference // so that if a user opens two sessions they don't get locking failures // this is OK for display style prefs (how many items, how they are sorted) // but might not be for more behavior-style prefs!! EOEditingContext ec = ERXEC.newEditingContext(); ec.lock(); try { EOEnterpriseObject pref = preferenceRecordForKey(key, ec); ERCoreUserInterface u = (ERCoreUserInterface)ERCoreBusinessLogic.actor(ec); if (pref != null) { if (value != null) { String encodedValue = encodedValue(value); if (ObjectUtils.notEqual(encodedValue, pref.valueForKey("value"))) { log.debug("Updating preference {}: {}={}", u, key, encodedValue); pref.takeValueForKey(encodedValue,"value"); } } else { log.debug("Removing preference {}: {}", u, key); ec.deleteObject(pref); } } else if (value!=null) { pref = ERXEOControlUtilities.createAndInsertObject(ec, "ERCPreference"); u.newPreference(pref); // done this way to not force you to sub-class our User entity pref.takeValueForKey(ERXEOControlUtilities.primaryKeyObjectForObject((EOEnterpriseObject)u),"userID"); pref.takeValueForKey(key,"key"); pref.takeValueForKey(encodedValue(value),"value"); if (log.isDebugEnabled()) log.debug("Creating preference {}: {} - {} -- {}", u, key, value, encodedValue(value)); } if (ec.hasChanges()) { ec.saveChanges(); } } catch(RuntimeException ex) { log.error("Error while setting preference {}", key, ex); } finally { ec.unlock(); } ec.dispose(); NSNotificationCenter.defaultCenter().postNotification(PreferenceDidChangeNotification, new NSDictionary(value, key)); } public boolean booleanValueForKey(String key) { return booleanValueForKeyWithDefault(key, false); } public boolean booleanValueForKeyWithDefault(String key, boolean def) { return ERXValueUtilities.booleanValueWithDefault(valueForKey(key), def); } public static class _UserPreferenceHandler { public _UserPreferenceHandler() { NSNotificationCenter.defaultCenter().addObserver(this, new NSSelector("handleBatchSizeChange", ERXConstant.NotificationClassArray), ERXBatchNavigationBar.BatchSizeChanged, null); NSNotificationCenter.defaultCenter().addObserver(this, new NSSelector("handleSortOrderingChange", ERXConstant.NotificationClassArray), ERXSortOrder.SortOrderingChanged, null); NSNotificationCenter.defaultCenter().addObserver(this, new NSSelector("handleDisplayVariantChange", ERXConstant.NotificationClassArray), ERDAttributeRepetition.DisplayVariantChanged, null); } public void handleBatchSizeChange(NSNotification n) { handleChange("batchSize", n); } public void handleSortOrderingChange(NSNotification n) { handleChange("sortOrdering", n); } public void handleDisplayVariantChange(NSNotification n) { handleChange("displayVariant", n); } public void handleChange(String prefName, NSNotification n) { if (ERCoreBusinessLogic.actor() != null) { NSKeyValueCoding context=(NSKeyValueCoding)n.userInfo().objectForKey("d2wContext"); if (context!=null && context.valueForKey("pageConfiguration") != null) { if ("displayVariant".equals(prefName)) { String key = prefName + "." + context.valueForKey("propertyKey") + "." + context.valueForKey("pageConfiguration"); userPreferences().takeValueForKey(n.object(), key); } else { userPreferences().takeValueForKey(n.object(), prefName+"."+(String)context.valueForKey("pageConfiguration")); } } } } } }