001/* 002 * Copyright (c) 2010-2026 Mark Allen, Norbert Bartels. 003 * 004 * Permission is hereby granted, free of charge, to any person obtaining a copy 005 * of this software and associated documentation files (the "Software"), to deal 006 * in the Software without restriction, including without limitation the rights 007 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 008 * copies of the Software, and to permit persons to whom the Software is 009 * furnished to do so, subject to the following conditions: 010 * 011 * The above copyright notice and this permission notice shall be included in 012 * all copies or substantial portions of the Software. 013 * 014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 019 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 020 * THE SOFTWARE. 021 */ 022package com.restfb.util; 023 024import static java.util.Comparator.comparingInt; 025 026import java.util.ArrayList; 027import java.util.Comparator; 028import java.util.List; 029 030import com.restfb.types.MessageTag; 031 032/** 033 * Helper to transform a Graph API {@code message} + {@code message_tags} pair into a normalized representation that 034 * uses the mention syntax (e.g. {@code @[12345]}). 035 */ 036public final class MessageTagUtils { 037 038 private MessageTagUtils() { 039 // prevent instantiation 040 } 041 042 /** 043 * Rebuilds a message so that tagged segments are replaced by their mention syntax. 044 * 045 * @param message 046 * original text returned by the Graph API 047 * @param tags 048 * message tags as returned by the Graph API 049 * @return the normalized message or the original one if the tags don't provide enough information 050 */ 051 public static String normalizeMessage(String message, List<MessageTag> tags) { 052 if (message == null || tags == null || tags.isEmpty()) { 053 return message; 054 } 055 056 List<MessageTag> applicableTags = new ArrayList<>(); 057 for (MessageTag tag : tags) { 058 if (isApplicable(tag)) { 059 applicableTags.add(tag); 060 } 061 } 062 063 if (applicableTags.isEmpty()) { 064 return message; 065 } 066 067 applicableTags.sort(tagComparator()); 068 069 StringBuilder normalized = new StringBuilder(message.length() + applicableTags.size() * 4); 070 int cursor = 0; 071 072 for (MessageTag tag : applicableTags) { 073 int start = tag.getOffset(); 074 int end = start + tag.getLength(); 075 076 if (start < cursor || start < 0 || end < start || end > message.length()) { 077 return message; 078 } 079 080 normalized.append(message, cursor, start); 081 normalized.append(buildMention(tag)); 082 cursor = end; 083 } 084 085 normalized.append(message.substring(cursor)); 086 return normalized.toString(); 087 } 088 089 private static boolean isApplicable(MessageTag tag) { 090 return tag != null && tag.getOffset() != null && tag.getLength() != null && tag.getOffset() >= 0 091 && tag.getLength() >= 0; 092 } 093 094 private static Comparator<MessageTag> tagComparator() { 095 return comparingInt(MessageTag::getOffset).thenComparing((a, b) -> Integer.compare(b.getLength(), a.getLength())); 096 } 097 098 private static String buildMention(MessageTag tag) { 099 String id = tag.getId(); 100 if (id != null && !id.isEmpty()) { 101 return "@[" + id + "]"; 102 } 103 104 String name = tag.getName(); 105 if (name != null) { 106 return name; 107 } 108 109 return ""; 110 } 111}