1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.fatwire.gst.foundation.facade.mda;
17
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.Enumeration;
23 import java.util.List;
24 import java.util.Locale;
25
26 import org.apache.commons.lang.StringUtils;
27 import org.apache.commons.logging.Log;
28
29 import COM.FutureTense.Interfaces.FTValList;
30 import COM.FutureTense.Interfaces.ICS;
31
32 import com.fatwire.assetapi.data.AssetId;
33 import com.fatwire.cs.core.db.PreparedStmt;
34 import com.fatwire.cs.core.db.StatementParam;
35 import com.fatwire.gst.foundation.facade.logging.LogUtil;
36 import com.fatwire.gst.foundation.facade.runtag.asset.AssetLoadByName;
37 import com.fatwire.gst.foundation.facade.runtag.render.LogDep;
38 import com.fatwire.gst.foundation.facade.sql.IListIterable;
39 import com.fatwire.gst.foundation.facade.sql.Row;
40 import com.fatwire.gst.foundation.facade.sql.SqlHelper;
41 import com.fatwire.mda.Dimension;
42 import com.fatwire.mda.DimensionException;
43 import com.fatwire.mda.DimensionFilterInstance;
44 import com.fatwire.mda.DimensionManager;
45 import com.fatwire.mda.DimensionSetInstance;
46 import com.fatwire.mda.DimensionableAssetManager;
47 import com.fatwire.system.Session;
48 import com.fatwire.system.SessionFactory;
49 import com.openmarket.xcelerate.asset.AssetIdImpl;
50
51 public class DefaultLocaleService implements LocaleService {
52 private static final Log LOG = LogUtil.getLog(DefaultLocaleService.class);
53
54
55
56 private final String localeVar;
57
58
59
60 private final String langVar;
61
62 private final ICS ics;
63
64 private Session session;
65
66
67
68
69
70
71
72 public DefaultLocaleService(final ICS ics) {
73 this(ics, "lang", "locale");
74 }
75
76
77
78
79
80
81
82
83
84
85
86 public DefaultLocaleService(ICS ics, String langVar, String localeVar) {
87 if (ics == null) {
88 throw new IllegalArgumentException("ICS must not be null.");
89 }
90
91 this.ics = ics;
92 this.langVar = langVar;
93 this.localeVar = localeVar;
94
95 }
96
97
98
99
100
101
102
103
104 @Override
105 public AssetId findTranslation(final String c, final String cid, final String site,
106 final String preferredLocaleDimensionId) {
107 return findTranslation(new AssetIdImpl(c, Long.parseLong(cid)), site,
108 Long.parseLong(preferredLocaleDimensionId));
109 }
110
111
112
113
114
115
116
117
118
119 @Override
120 public AssetId findTranslation(final AssetId id, final String site, final long preferredDimension) {
121
122 final long dimensionSetId = locateDimensionSetForSite(site);
123
124 return findTranslation(id, preferredDimension, dimensionSetId);
125 }
126
127
128
129
130
131
132
133
134
135 @Override
136 public AssetId findTranslation(final AssetId id, final long preferredDimension, final long dimensionSetId) {
137 if (id == null) {
138 throw new IllegalArgumentException("Required Asset ID missing");
139 }
140
141 if (_isInputAssetDimensionPreferred(id, preferredDimension)) {
142 LOG.debug("Input dimension is already in the preferred dimension. Not invoking dimension set filter. Asset: "
143 + id + ", dimension: " + preferredDimension);
144 return id;
145 } else {
146 LOG.debug("About to look for translations. Input asset id: " + id + ", dimension set: " + dimensionSetId
147 + ", preferred dimension: " + preferredDimension);
148 }
149
150
151
152
153 final DimensionSetInstance dimset = _getDimensionSet(dimensionSetId);
154 return findTranslation(id, preferredDimension, dimset);
155
156 }
157
158
159
160
161
162
163
164
165
166 @Override
167 public AssetId findTranslation(final AssetId id, final long preferredDimension, final String dimensionSetName) {
168 if (id == null) {
169 throw new IllegalArgumentException("Required Asset ID missing");
170 }
171 final Dimension locale = getLocaleForAsset(id);
172
173 if (locale == null) {
174 LOG.debug("Asset is not localized. Not invoking dimension set filter. Asset: " + id);
175 return id;
176 }
177 if (locale.getId().getId() == preferredDimension) {
178 LOG.debug("Input dimension is already in the preferred dimension. Not invoking dimension set filter. Asset: "
179 + id + ", dimension: " + preferredDimension);
180 return id;
181 } else {
182 LOG.debug("About to look for translations. Input asset id: " + id + ", dimension set: " + dimensionSetName
183 + ", preferred dimension: " + preferredDimension);
184 }
185
186
187
188
189 final DimensionSetInstance dimset = getDimensionSet(dimensionSetName);
190
191 return findTranslation(id, preferredDimension, dimset);
192 }
193
194
195
196
197
198
199
200
201
202
203 @Override
204 public AssetId findTranslation(final AssetId id, final long preferredDimension, final DimensionSetInstance dimset) {
205
206 final DimensionFilterInstance filter = _getPopulatedDimensionFilter(dimset, preferredDimension);
207 AssetId translated = findTranslation(id, filter);
208 if (translated == null)
209 LOG.warn("No translation found for asset " + id + " in dimension set " + dimset.getId() + " for dimension "
210 + preferredDimension + ".");
211 return translated;
212 }
213
214
215
216
217 private boolean _isInputAssetDimensionPreferred(final AssetId id, final long preferredDimension) {
218 final Dimension dim = getLocaleForAsset(id);
219 if (dim == null) {
220 return true;
221 }
222
223 return dim.getId().getId() == preferredDimension;
224 }
225
226 private static final PreparedStmt FIND_DIMSET_FOR_SITE_PREPAREDSTMT = new PreparedStmt(
227 "select ds.id as id from DimensionSet ds, Publication p, AssetPublication ap where p.name = ? and p.id = ap.pubid and ap.assetid = ds.id and ds.status != 'VO' order by ds.updateddate",
228 Arrays.asList("DimensionSet", "AssetPublication", "Publication"));
229
230 static {
231 FIND_DIMSET_FOR_SITE_PREPAREDSTMT.setElement(0, "Publication", "name");
232 }
233
234
235
236
237
238
239
240
241
242 public long locateDimensionSetForSite(final String site) {
243 if (StringUtils.isBlank(site)) {
244 throw new IllegalArgumentException("Required site name missing");
245 }
246 final StatementParam params = FIND_DIMSET_FOR_SITE_PREPAREDSTMT.newParam();
247 params.setString(0, site);
248 final IListIterable list = SqlHelper.select(ics, FIND_DIMSET_FOR_SITE_PREPAREDSTMT, params);
249
250 final int numRows = list.size();
251 if (numRows == 0) {
252 throw new IllegalStateException(
253 "A DimensionSet has not been defined for site '"
254 + site
255 + "'. Cannot determine any translation unless some locales (Dimensions) are enabled for that site. Aborting operation.");
256 }
257 if (numRows > 1) {
258 final StringBuilder msg = new StringBuilder("More than one dimension set found in site " + site
259 + ". Exactly one is expected. Dimension set ids: ");
260 for (final Row row : list) {
261 final String id = row.getString("id");
262 LogDep.logDep(ics, "DimensionSet", id);
263 msg.append(id).append(" ");
264 }
265 throw new IllegalStateException(msg.append(".").toString());
266 }
267
268 final String id = list.iterator().next().getString("id");
269 LogDep.logDep(ics, "DimensionSet", id);
270 return Long.parseLong(id);
271 }
272
273 private DimensionFilterInstance _getPopulatedDimensionFilter(final DimensionSetInstance dimset,
274 final long dimensionId) {
275
276
277
278
279
280 final Dimension preferredDimension = getDM().loadDimension(dimensionId);
281 if (preferredDimension == null) {
282 throw new RuntimeException("Attempted to load Dimension with id " + dimensionId + " but it came back null");
283 }
284 return _getPopulatedDimensionFilter(dimset, preferredDimension);
285 }
286
287 private DimensionFilterInstance _getPopulatedDimensionFilter(final DimensionSetInstance dimset,
288 final Dimension preferredDimension) {
289 DimensionFilterInstance filter;
290 try {
291 filter = dimset.getFilter();
292 } catch (final DimensionException e) {
293 throw new RuntimeException("Could not get Dimension Filter from DimensionSet", e);
294 }
295
296 filter.setDimensonPreference(Collections.singletonList(preferredDimension));
297 return filter;
298 }
299
300 private DimensionSetInstance _getDimensionSet(final long dimSetId) {
301 final String DIMSET_OBJ_NAME = "LocaleUtils:findTranslation:theDimensionSet:DimensionSet";
302
303 ics.SetObj(DIMSET_OBJ_NAME, null);
304 final FTValList args = new FTValList();
305 args.put("NAME", DIMSET_OBJ_NAME);
306 args.put("TYPE", "DimensionSet");
307 args.put("OBJECTID", Long.toString(dimSetId));
308 args.put("EDITABLE", "FALSE");
309 ics.runTag("ASSET.LOAD", args);
310
311 if (ics.GetErrno() < 0) {
312 throw new IllegalStateException("Could not load dimension set. Errno: " + ics.GetErrno());
313 }
314
315 final Object o = ics.GetObj(DIMSET_OBJ_NAME);
316 ics.SetObj(DIMSET_OBJ_NAME, null);
317 if (o == null) {
318 throw new IllegalStateException("Could not load dimension set but we got no errno... unexpected...");
319 }
320
321 DimensionSetInstance dimset;
322 if (o instanceof DimensionSetInstance) {
323 dimset = (DimensionSetInstance) o;
324 } else {
325 throw new IllegalStateException("Loaded an asset that is not a DimensionSetInstance.");
326 }
327 return dimset;
328 }
329
330
331
332
333
334
335
336
337
338 @Override
339 public DimensionSetInstance getDimensionSet(final String name) {
340 final String DIMSET_OBJ_NAME = "LocaleUtils:findTranslation:theDimensionSet:DimensionSet";
341 ics.SetObj(DIMSET_OBJ_NAME, null);
342 final AssetLoadByName a = new AssetLoadByName();
343 a.setAssetType("DimensionSet");
344 a.setAssetName(name);
345 a.setEditable(false);
346 a.setName(DIMSET_OBJ_NAME);
347 a.execute(ics);
348
349 if (ics.GetErrno() < 0) {
350 throw new IllegalStateException("Could not load dimension set. Errno: " + ics.GetErrno());
351 }
352
353 final Object o = ics.GetObj(DIMSET_OBJ_NAME);
354 ics.SetObj(DIMSET_OBJ_NAME, null);
355 if (o == null) {
356 throw new IllegalStateException("Could not load dimension set but we got no errno... unexpected...");
357 }
358
359 DimensionSetInstance dimset;
360 if (o instanceof DimensionSetInstance) {
361 dimset = (DimensionSetInstance) o;
362 } else {
363 throw new IllegalStateException("Loaded an asset that is not a DimensionSetInstance");
364 }
365 return dimset;
366 }
367
368 private DimensionableAssetManager dam;
369
370
371
372
373
374
375 public DimensionableAssetManager getDAM() {
376 if (dam == null) {
377 dam = getManager(DimensionableAssetManager.class);
378 }
379 return dam;
380 }
381
382 private DimensionManager dm;
383
384
385
386
387
388
389 protected DimensionManager getDM() {
390 if (dm == null) {
391 dm = getManager(DimensionManager.class);
392 }
393 return dm;
394 }
395
396
397
398
399
400
401
402
403
404 @Override
405 public Dimension getLocaleForAsset(final AssetId id) {
406 final Collection<Dimension> dims = getDAM().getDimensionsForAsset(id);
407 for (final Dimension dim : dims) {
408 if ("locale".equalsIgnoreCase(dim.getGroup())) {
409 return dim;
410 }
411 }
412 return null;
413 }
414
415
416
417
418
419
420
421
422
423 @Override
424 public long getDimensionIdForName(final String name) {
425 final AssetId id = getDimensionAssetIdForName(name);
426 return id == null ? -1 : id.getId();
427 }
428
429
430
431
432
433
434
435
436 @Override
437 public AssetId getDimensionAssetIdForName(final String name) {
438 final Dimension dim = getDimensionForName(name);
439 return dim == null ? null : dim.getId();
440 }
441
442
443
444
445
446
447
448
449
450 @Override
451 public Dimension getDimensionForName(final String name) {
452 return getDM().loadDimension(name);
453
454 }
455
456
457
458
459
460
461
462
463
464 @Override
465 public String getNameForDimensionId(final long dimensionid) {
466 final Dimension dim = getDM().loadDimension(dimensionid);
467 return dim == null ? null : dim.getName();
468 }
469
470 protected Session getSession() {
471 if (session == null) {
472 session = SessionFactory.getSession(ics);
473
474 }
475 return session;
476 }
477
478 @SuppressWarnings("unchecked")
479 protected <T> T getManager(final Class<T> c) {
480 return (T) getSession().getManager(c.getName());
481 }
482
483 @Override
484 public AssetId findTranslation(AssetId id, DimensionFilterInstance filter) {
485
486 final Collection<AssetId> relatives = getDAM().getRelatives(id, filter, "Locale");
487
488
489
490 if (relatives == null) {
491 LOG.debug("No translation found for asset " + id + ".");
492 return null;
493 } else {
494 switch (relatives.size()) {
495 case 0: {
496 LOG.debug("No translation found for " + id + ".");
497
498
499
500
501
502
503
504 return null;
505 }
506 case 1: {
507 final AssetId relative = relatives.iterator().next();
508 LOG.trace("LocaleUtils.findTranslation: RELATIVE FOUND... " + relative.getType() + " '"
509 + relative.getId() + "' // errno = " + ics.GetErrno());
510 return relative;
511
512 }
513 default: {
514 throw new IllegalStateException("found more than one translation for asset " + id
515 + " and that is not supposed to be possible.");
516 }
517 }
518 }
519 }
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552 public DimensionFilterInstance getDimensionFilter(String site) {
553
554 DimensionFilterInstance filter;
555 try {
556 DimensionSetInstance dimSet = locateDimensionSetInstanceForSite(site);
557 if (dimSet == null) {
558 if (LOG.isTraceEnabled()) {
559 LOG.trace("no DimensionSet returned from getDimensionSet().");
560 }
561 return null;
562 }
563 Collection<AssetId> preferredLocales = getPreferredLocales();
564
565 filter = DimensionUtils.getDimensionFilter(DimensionUtils.getDM(ics), preferredLocales, dimSet);
566 if (LOG.isDebugEnabled()) {
567 LOG.debug("Located dimension filter: " + filter + " in dimensionSet " + dimSet
568 + " with preferred locales: " + preferredLocales + " ");
569 }
570 } catch (DimensionException e) {
571 LOG.error("Could not locate dimension filter", e);
572 filter = null;
573 } catch (RuntimeException e) {
574 LOG.error("Could not locate dimension filter", e);
575 filter = null;
576 }
577 return filter;
578 }
579
580
581
582
583
584
585
586
587 protected final AssetId getExplicitlySpecifiedLocale() {
588
589 String localeName = ics.GetVar(langVar);
590
591 if (StringUtils.isNotBlank(localeName)) {
592 Dimension d = DimensionUtils.getDimensionForName(ics, localeName);
593 if (d != null) {
594 LOG.trace("Preferred locale explicitly set to " + localeName);
595 return d.getId();
596 }
597 }
598 return null;
599 }
600
601
602
603
604
605
606
607 protected final Collection<AssetId> getPreferredLocales() {
608 AssetId result = getExplicitlySpecifiedLocale();
609 if (result != null)
610 return Collections.singleton(result);
611
612
613 String l = ics.GetVar(localeVar);
614 try {
615 long localeIdFromVar = Long.parseLong(l);
616 DimensionManager dm = DimensionUtils.getDM(ics);
617 Dimension d = dm.loadDimension(localeIdFromVar);
618 if (d != null) {
619 LOG.trace("Preferred locale detected in ICS context using 'locale' variable: " + localeIdFromVar);
620 return Collections.singletonList(d.getId());
621 }
622 } catch (NumberFormatException e) {
623
624 try {
625 Dimension d = DimensionUtils.getDimensionForName(ics, localeVar);
626 if (d != null) {
627 LOG.trace("Preferred locale detected in ICS context using 'locale' variable: " + localeVar);
628 return Collections.singletonList(d.getId());
629 }
630 } catch (Exception ex) {
631
632 }
633 }
634
635
636
637 String localeSSVar = ics.GetSSVar(localeVar);
638 try {
639 long localeIdFromSSVar = Long.parseLong(localeSSVar);
640 DimensionManager dm = DimensionUtils.getDM(ics);
641 Dimension d = dm.loadDimension(localeIdFromSSVar);
642 if (d != null) {
643 LOG.trace("Preferred locale detected in ICS context using 'locale' session variable: "
644 + localeIdFromSSVar);
645 return Collections.singletonList(d.getId());
646 }
647 } catch (NumberFormatException e) {
648
649 try {
650 Dimension d = DimensionUtils.getDimensionForName(ics, localeSSVar);
651 if (d != null) {
652 LOG.trace("Preferred locale detected in ICS context using 'locale' session variable: "
653 + localeSSVar);
654 return Collections.singletonList(d.getId());
655 }
656 } catch (Exception ex) {
657
658 }
659 }
660
661
662
663 List<AssetId> preferredLocales = new ArrayList<AssetId>();
664 @SuppressWarnings({ "rawtypes", "deprecation" })
665 Enumeration locales = ics.getIServlet().getServletRequest().getLocales();
666 while (locales.hasMoreElements()) {
667 Locale locale = (Locale) locales.nextElement();
668 if (locale != null) {
669 String localeName = locale.toString();
670 if (localeName != null && localeName.length() > 0) {
671 try {
672 Dimension dimension = DimensionUtils.getDimensionForName(ics, localeName);
673 if (dimension != null) {
674 preferredLocales.add(dimension.getId());
675 LOG.trace("Found registered locale in user's Accept-Language header (or default): "
676 + localeName);
677 }
678 } catch (RuntimeException e) {
679
680
681
682 LOG.trace(
683 "Found a locale in the user's Accept-Language header, but it was not registered as a dimension: "
684 + localeName + " (this is not usually an error)", e);
685 }
686 }
687 }
688 }
689 return preferredLocales;
690 }
691
692
693
694
695
696
697
698
699 public final DimensionSetInstance locateDimensionSetInstanceForSite(String site) {
700 try {
701 if (StringUtils.isNotBlank(site)) {
702 long discoveredId = locateDimensionSetForSite(site);
703 LOG.trace("Auto-discovered dimension set because there is only one in site " + site + ": DimensionSet:"
704 + discoveredId);
705 return _getDimensionSet(discoveredId);
706 }
707 } catch (RuntimeException e) {
708 if (LOG.isTraceEnabled())
709 LOG.trace("Could not auto-discover dimensionset: " + e);
710 }
711 return null;
712 }
713 }